MIDAS
Loading...
Searching...
No Matches
msequencer.cxx
Go to the documentation of this file.
1/********************************************************************\
2
3 Name: msequencer.cxx
4 Created by: Stefan Ritt
5
6 Contents: MIDAS sequencer engine
7
8\********************************************************************/
9
10#include "midas.h"
11#include "msystem.h"
12#include "mvodb.h"
13#include "mxml.h"
14#include "sequencer.h"
15#include "mstrlcpy.h"
16#include "tinyexpr.h"
17#include "odbxx.h"
18#include <assert.h>
19#include <iostream>
20#include <fstream>
21#include <sstream>
22#include <string.h>
23#include <vector>
24
25#define XNAME_LENGTH 256
26
40/*------------------------------------------------------------------*/
41
42SEQUENCER_STR(sequencer_str);
43
44MVOdb *gOdb = NULL;
45
46// sequencer context
47
48class SeqCon
49{
50public:
51 PMXML_NODE pnseq = NULL;
52 MVOdb *odbs = NULL;
53 SEQUENCER *seqp = NULL;
56 std::string seq_name;
57 std::string prg_name;
58 std::string odb_path;
59};
60
61/*------------------------------------------------------------------*/
62
63char *stristr(const char *str, const char *pattern) {
64 char c1, c2, *ps, *pp;
65
66 if (str == NULL || pattern == NULL)
67 return NULL;
68
69 while (*str) {
70 ps = (char *) str;
71 pp = (char *) pattern;
72 c1 = *ps;
73 c2 = *pp;
74 if (toupper(c1) == toupper(c2)) {
75 while (*pp) {
76 c1 = *ps;
77 c2 = *pp;
78
79 if (toupper(c1) != toupper(c2))
80 break;
81
82 ps++;
83 pp++;
84 }
85
86 if (!*pp)
87 return (char *) str;
88 }
89 str++;
90 }
91
92 return NULL;
93}
94
95/*------------------------------------------------------------------*/
96
97void strsubst(char *string, int size, const char *pattern, const char *subst)
98/* subsitute "pattern" with "subst" in "string" */
99{
100 char *tail, *p;
101 int s;
102
103 p = string;
104 for (p = stristr(p, pattern); p != NULL; p = stristr(p, pattern)) {
105
106 if (strlen(pattern) == strlen(subst)) {
107 memcpy(p, subst, strlen(subst));
108 } else if (strlen(pattern) > strlen(subst)) {
109 memcpy(p, subst, strlen(subst));
110 memmove(p + strlen(subst), p + strlen(pattern), strlen(p + strlen(pattern)) + 1);
111 } else {
112 tail = (char *) malloc(strlen(p) - strlen(pattern) + 1);
113 strcpy(tail, p + strlen(pattern));
114 s = size - (p - string);
115 mstrlcpy(p, subst, s);
116 mstrlcat(p, tail, s);
117 free(tail);
118 tail = NULL;
119 }
120
121 p += strlen(subst);
122 }
123}
124
125/*------------------------------------------------------------------*/
126
127static std::string toString(int v) {
128 char buf[256];
129 snprintf(buf, sizeof(buf), "%d", v);
130 return buf;
131}
132
133static std::string qtoString(std::string filename, int line, int level) {
134 std::string buf = "l=\"" + std::to_string(line) + "\"";
135 buf += " fn=\"" + filename + "\"";
136 if (level)
137 buf += " lvl=\"" + std::to_string(level) + "\"";
138 return buf;
139}
140
141static std::string q(const char *s) {
142 return "\"" + std::string(s) + "\"";
143}
144
145/*------------------------------------------------------------------*/
146
147bool is_valid_number(const char *str) {
148 std::string s(str);
149 std::stringstream ss;
150 ss << s;
151 double num = 0;
152 ss >> num;
153 if (ss.good())
154 return false;
155 else if (num == 0 && s[0] != '0')
156 return false;
157 else if (s[0] == 0)
158 return false;
159 return true;
160}
161
162/*------------------------------------------------------------------*/
163
164void seq_error(SEQUENCER &seq, SeqCon* c, const char *str) {
165 int status;
166 HNDLE hKey;
167
168 mstrlcpy(seq.error, str, sizeof(seq.error));
171 seq.running = FALSE;
173
174 status = db_find_key(c->hDB, c->hSeq, "State", &hKey);
175 if (status != DB_SUCCESS)
176 return;
177 status = db_set_record(c->hDB, hKey, &seq, sizeof(seq), 0);
178 if (status != DB_SUCCESS)
179 return;
180
181 cm_msg(MTALK, "sequencer", "Sequencer has stopped with error.");
182}
183
184/*------------------------------------------------------------------*/
185
186int strbreak(char *str, char list[][XNAME_LENGTH], int size, const char *brk, BOOL ignore_quotes)
187/* break comma-separated list into char array, stripping leading
188 and trailing blanks */
189{
190 int i, j;
191 char *p;
192
193 memset(list, 0, size * XNAME_LENGTH);
194 p = str;
195 if (!p || !*p)
196 return 0;
197
198 while (*p == ' ')
199 p++;
200
201 for (i = 0; *p && i < size; i++) {
202 if (*p == '"' && !ignore_quotes) {
203 p++;
204 j = 0;
205 memset(list[i], 0, XNAME_LENGTH);
206 do {
207 /* convert two '"' to one */
208 if (*p == '"' && *(p + 1) == '"') {
209 list[i][j++] = '"';
210 p += 2;
211 } else if (*p == '"') {
212 break;
213 } else
214 list[i][j++] = *p++;
215
216 } while (j < XNAME_LENGTH - 1);
217 list[i][j] = 0;
218
219 /* skip second '"' */
220 p++;
221
222 /* skip blanks and break character */
223 while (*p == ' ')
224 p++;
225 if (*p && strchr(brk, *p))
226 p++;
227 while (*p == ' ')
228 p++;
229
230 } else {
231 mstrlcpy(list[i], p, XNAME_LENGTH);
232
233 for (j = 0; j < (int) strlen(list[i]); j++)
234 if (strchr(brk, list[i][j])) {
235 list[i][j] = 0;
236 break;
237 }
238
239 p += strlen(list[i]);
240 while (*p == ' ')
241 p++;
242 if (*p && strchr(brk, *p))
243 p++;
244 while (*p == ' ')
245 p++;
246
247 while (list[i][strlen(list[i]) - 1] == ' ')
248 list[i][strlen(list[i]) - 1] = 0;
249 }
250
251 if (!*p)
252 break;
253 }
254
255 if (i == size)
256 return size;
257
258 return i + 1;
259}
260
261/*------------------------------------------------------------------*/
262
263extern char *stristr(const char *str, const char *pattern);
264
265/*------------------------------------------------------------------*/
266
267std::string eval_var(SEQUENCER &seq, SeqCon* c, std::string value) {
268 std::string result;
269 std::string xpath;
270 xpath += "/";
271 xpath += c->odb_path;
272
273 //printf("eval [%s] xpath [%s]\n", value.c_str(), xpath.c_str());
274
275 result = value;
276
277 // replace all $... with value
278 int i1, i2;
279 std::string vsubst;
280 while ((i1 = (int)result.find("$")) != (int)std::string::npos) {
281 std::string s = result.substr(i1 + 1);
282 if (std::isdigit(s[0])) {
283 // find end of number
284 for (i2 = i1 + 1; std::isdigit(result[i2]);)
285 i2++;
286
287 // replace all $<number> with subroutine parameters
288 int index = atoi(s.c_str());
289 if (seq.stack_index > 0) {
290 std::istringstream f(seq.subroutine_param[seq.stack_index - 1]);
291 std::vector<std::string> param;
292 std::string sp;
293 while (std::getline(f, sp, ','))
294 param.push_back(sp);
295 if (index == 0 || index > (int)param.size())
296 throw "Parameter \"$" + std::to_string(index) + "\" not valid";
297 vsubst = param[index - 1];
298 if (vsubst[0] == '$')
299 vsubst = eval_var(seq, c, vsubst);
300 } else
301 throw "Parameter \"$" + std::to_string(index) + "\" not valid";
302 } else {
303 // find end of string
304 for (i2 = i1 + 1; std::isalnum(result[i2]) || result[i2] == '_';)
305 i2++;
306 s = s.substr(0, i2 - i1 - 1);
307 if (result[i2] == '[') {
308 // array
309 auto sindex = result.substr(i2+1);
310 int i = sindex.find(']');
311 if (i == (int)std::string::npos)
312 throw "Variable \"" + result +"\" does not contain ']'";
313 sindex = sindex.substr(0, i);
314 sindex = eval_var(seq, c, sindex);
315 int index;
316 try {
317 index = std::stoi(sindex);
318 } catch (...) {
319 throw "Variable \"" + s + "\" has invalid index";
320 }
321
322 try {
323 midas::odb o(xpath + "/Variables/" + s);
324 std::vector<std::string> sv = o;
325 vsubst = sv[index];
326 } catch (...) {
327 throw "Variable \"" + s + "\" not found";
328 }
329 while (result[i2] && result[i2] != ']')
330 i2++;
331 if (!result[i2])
332 throw "Variable \"" + result +"\" does not contain ']'";
333 if (result[i2] == ']')
334 i2++;
335 } else {
336 try {
337 if (!midas::odb::exists(xpath + "/Variables/" + s))
338 throw "Variable \"" + s + "\" not found";
339 midas::odb o(xpath + "/Variables/" + s);
340 vsubst = o;
341 } catch (...) {
342 throw "Variable \"" + s + "\" not found";
343 }
344 }
345 }
346
347 result = result.substr(0, i1) + vsubst + result.substr(i2);
348 }
349
350 //printf("eval [%s] xpath [%s] result [%s]\n", value.c_str(), xpath.c_str(), result.c_str());
351
352 // check if result is a list
353 if (result.find(",") != std::string::npos)
354 return result;
355
356 // check for expression
357 int error;
358 double r = te_interp(result.c_str(), &error);
359 if (error > 0) {
360 // check if result is only a string
361 if (!std::isdigit(result[0]) && result[0] != '-')
362 return result;
363
364 throw "Error in expression \"" + result + "\" position " + std::to_string(error - 1);
365 }
366
367 if (r == (int) r)
368 return std::to_string((int) r);
369
370 return std::to_string(r);
371}
372
373/*------------------------------------------------------------------*/
374
375int concatenate(SEQUENCER &seq, SeqCon* c, char *result, int size, char *value) {
376 char list[100][XNAME_LENGTH];
377 int i, n;
378
379 n = strbreak(value, list, 100, ",", FALSE);
380
381 result[0] = 0;
382 for (i = 0; i < n; i++) {
383 std::string str = eval_var(seq, c, std::string(list[i]));
384 mstrlcat(result, str.c_str(), size);
385 }
386
387 return TRUE;
388}
389
390/*------------------------------------------------------------------*/
391
392int eval_condition(SEQUENCER &seq, SeqCon* c, const char *condition) {
393 int i;
394 double value1, value2;
395 char value1_str[256], value2_str[256], str[256], op[3], *p;
396 std::string value1_var, value2_var;
397
398 // strip leading and trailing space
399 p = (char *)condition;
400 while (*p == ' ')
401 p++;
402 strcpy(str, p);
403
404 // strip any comment '#'
405 if (strchr(str, '#'))
406 *strchr(str, '#') = 0;
407
408 while (strlen(str) > 0 && (str[strlen(str)-1] == ' '))
409 str[strlen(str)-1] = 0;
410
411 // strip enclosing '()'
412 if (str[0] == '(' && strlen(str) > 0 && str[strlen(str)-1] == ')') {
413 mstrlcpy(value1_str, str+1, sizeof(value1_str));
414 mstrlcpy(str, value1_str, sizeof(str));
415 str[strlen(str)-1] = 0;
416 }
417
418 op[1] = op[2] = 0;
419
420 /* find value and operator */
421 for (i = 0; i < (int) strlen(str); i++)
422 if (strchr("<>=!&", str[i]) != NULL)
423 break;
424 mstrlcpy(value1_str, str, i + 1);
425 while (value1_str[strlen(value1_str) - 1] == ' ')
426 value1_str[strlen(value1_str) - 1] = 0;
427 op[0] = str[i];
428 if (strchr("<>=!&", str[i + 1]) != NULL)
429 op[1] = str[++i];
430
431 for (i++; str[i] == ' '; i++);
432 mstrlcpy(value2_str, str + i, sizeof(value2_str));
433
434 value1_var = eval_var(seq, c, value1_str);
435 value2_var = eval_var(seq, c, value2_str);
436
437 if (!is_valid_number(value1_var.c_str()) || !is_valid_number(value2_var.c_str())) {
438 // string comparison
439 if (strcmp(op, "=") == 0)
440 return equal_ustring(value1_var.c_str(), value2_var.c_str()) ? 1 : 0;
441 if (strcmp(op, "==") == 0)
442 return equal_ustring(value1_var.c_str(), value2_var.c_str()) ? 1 : 0;
443 if (strcmp(op, "!=") == 0)
444 return equal_ustring(value1_var.c_str(), value2_var.c_str()) ? 0 : 1;
445 // invalid operator for string comparisons
446 return -1;
447 }
448
449 // numeric comparison
450 value1 = atof(value1_var.c_str());
451 value2 = atof(value2_var.c_str());
452
453 /* now do logical operation */
454 if (strcmp(op, "=") == 0)
455 if (value1 == value2)
456 return 1;
457 if (strcmp(op, "==") == 0)
458 if (value1 == value2)
459 return 1;
460 if (strcmp(op, "!=") == 0)
461 if (value1 != value2)
462 return 1;
463 if (strcmp(op, "<") == 0)
464 if (value1 < value2)
465 return 1;
466 if (strcmp(op, ">") == 0)
467 if (value1 > value2)
468 return 1;
469 if (strcmp(op, "<=") == 0)
470 if (value1 <= value2)
471 return 1;
472 if (strcmp(op, ">=") == 0)
473 if (value1 >= value2)
474 return 1;
475 if (strcmp(op, "&") == 0)
476 if (((unsigned int) value1 & (unsigned int) value2) > 0)
477 return 1;
478
479 return 0;
480}
481
482/*------------------------------------------------------------------*/
483
484bool loadMSL(MVOdb *o, const std::string &filename) {
485
486 std::ifstream file(filename);
487 if (!file.is_open())
488 return false;
489
490 std::vector<std::string> lines;
491 std::string line;
492
493 // read all lines from file and put it into array
494 while (std::getline(file, line))
495 lines.push_back(line);
496 file.close();
497
498 o->WSA("Script/Lines", lines, 0);
499
500 return true;
501}
502
503/*------------------------------------------------------------------*/
504
505static BOOL msl_parse(SEQUENCER& seq, SeqCon*c, int level, const char *cpath, const char *filename, const char *xml_filename,
506 char *error, int error_size, int *error_line) {
507 char str[256], *pl, *pe;
508 char list[100][XNAME_LENGTH], list2[100][XNAME_LENGTH], **lines;
509 int i, j, n, nq, n_lines, endl, line, nest, incl, library;
510 std::string xml, path, fn;
511 char *msl_include, *xml_include, *include_error;
512 int include_error_size;
513 BOOL include_status, rel_path;
514
515 if (level > 10) {
516 snprintf(error, error_size, "More than 10 nested INCLUDE statements exceeded in file \"%s\"", filename);
517 return FALSE;
518 }
519
520 rel_path = (filename[0] != DIR_SEPARATOR);
521 path = std::string(cpath);
522 if (path.length() > 0 && path.back() != '/')
523 path += '/';
524
525 std::string fullFilename;
526 if (rel_path)
527 fullFilename = path + filename;
528 else
529 fullFilename = filename;
530
531 int fhin = open(fullFilename.c_str(), O_RDONLY | O_TEXT);
532 if (fhin < 0) {
533 snprintf(error, error_size, "Cannot open \"%s\", errno %d (%s)", fullFilename.c_str(), errno, strerror(errno));
534 return FALSE;
535 }
536
537 off_t off = lseek(fhin, 0, SEEK_END);
538 if (off < 0) {
539 snprintf(error, error_size, "Cannot find size of \"%s\", lseek(SEEK_END) errno %d (%s)", fullFilename.c_str(), errno, strerror(errno));
540 close(fhin);
541 return FALSE;
542 }
543
544 lseek(fhin, 0, SEEK_SET);
545
546 std::string s;
547 if (rel_path)
548 s = path + xml_filename;
549 else
550 s = xml_filename;
551
552 FILE *fout = fopen(s.c_str(), "wt");
553 if (fout == NULL) {
554 snprintf(error, error_size, "Cannot write to \"%s\", fopen() errno %d (%s)", s.c_str(), errno, strerror(errno));
555 close(fhin);
556 return FALSE;
557 }
558
559 size_t size = off;
560
561 char* buf = (char *) malloc(size + 1);
562 ssize_t rd = read(fhin, buf, size);
563
564 if (rd < 0) {
565 snprintf(error, error_size, "Cannot read \"%s\", read(%zu) errno %d (%s)", fullFilename.c_str(), size, errno, strerror(errno));
566 size = 0;
567 } else {
568 size = rd;
569 }
570
571 buf[size] = 0;
572 close(fhin);
573
574 /* look for any includes */
575 lines = (char **) malloc(sizeof(char *));
576 incl = 0;
577 pl = buf;
578 library = FALSE;
579 for (n_lines = 0; *pl; n_lines++) {
580 lines = (char **) realloc(lines, sizeof(char *) * (n_lines + 1));
581 lines[n_lines] = pl;
582 if (strchr(pl, '\n')) {
583 pe = strchr(pl, '\n');
584 *pe = 0;
585 if (*(pe - 1) == '\r') {
586 *(pe - 1) = 0;
587 }
588 pe++;
589 } else
590 pe = pl + strlen(pl);
591 mstrlcpy(str, pl, sizeof(str));
592 pl = pe;
593 strbreak(str, list, 100, ", ", FALSE);
594 if (equal_ustring(list[0], "include")) {
595 if (!incl) {
596 xml += "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
597 xml += "<!DOCTYPE RunSequence [\n";
598 incl = 1;
599 }
600
601 // if a path is given, use filename as entity reference
602 char *reference = strrchr(list[1], '/');
603 if (reference)
604 reference++;
605 else
606 reference = list[1];
607
608 if (strchr(reference, '.') && !strstr(reference, ".msl") && !strstr(reference, ".MSL")) {
609 snprintf(error, error_size, "Include file \"%s\" cannot have an extension other than .msl", reference);
610 return FALSE;
611 }
612
613 std::string p;
614 if (list[1][0] == '/') {
615 p = std::string(cm_get_path());
616 p += "userfiles/sequencer";
617 p += list[1];
618 } else {
619 p = path;
620 if (!p.empty() && p.back() != '/')
621 p += "/";
622 p += list[1];
623 }
624
625 xml += " <!ENTITY ";
626 xml += reference;
627 xml += " SYSTEM \"";
628 xml += p;
629 xml += ".xml\">\n";
630
631 // recurse
632 size = p.length() + 1 + 4;
633 msl_include = (char *) malloc(size);
634 xml_include = (char *) malloc(size);
635 mstrlcpy(msl_include, p.c_str(), size);
636 mstrlcpy(xml_include, p.c_str(), size);
637 if (!strstr(msl_include, ".msl"))
638 mstrlcat(msl_include, ".msl", size);
639 mstrlcat(xml_include, ".xml", size);
640
641 include_error = error + strlen(error);
642 include_error_size = error_size - strlen(error);
643
644 std::string path(cm_get_path());
645 path += "userfiles/sequencer/";
646 path += seq.path;
647
648 include_status = msl_parse(seq, c, level+1, path.c_str(), msl_include, xml_include, include_error, include_error_size, error_line);
649 free(msl_include);
650 free(xml_include);
651
652 if (!include_status) {
653 // report the error on CALL line instead of the one in included file
654 *error_line = n_lines + 1;
655 return FALSE;
656 }
657 }
658 if (equal_ustring(list[0], "library")) {
659 xml += "<Library name=\"";
660 xml += list[1];
661 xml += "\">\n";
662 library = TRUE;
663 }
664 }
665 if (incl) {
666 xml += "]>\n";
667 } else if (!library) {
668 xml += "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
669 }
670
671 /* parse rest of file */
672 if (!library) {
673 xml += "<RunSequence xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"\">\n";
674 }
675
676 std::vector<std::string> slines;
677 for (line = 0; line < n_lines; line++) {
678 slines.push_back(lines[line]);
679 }
680
681 c->odbs->WSA("Script/Lines", slines, 0);
682
683 /* save parameters */
684 std::string p = "/" + c->odb_path + "/Param/Value";
685 midas::odb oldSeqParam(p);
686 oldSeqParam.set_auto_refresh_read(false);
687 oldSeqParam.set_auto_create(false);
688
689 /* clear all variables */
690 if (!seq.running) {
691 c->odbs->Delete("Variables");
692 c->odbs->Delete("Param");
693 }
694
695 for (line = 0; line < n_lines; line++) {
696 char *p = lines[line];
697 while (*p == ' ')
698 p++;
699 mstrlcpy(list[0], p, sizeof(list[0]));
700 if (strchr(list[0], ' '))
701 *strchr(list[0], ' ') = 0;
702 p += strlen(list[0]);
703 n = strbreak(p + 1, &list[1], 99, ",", FALSE) + 1;
704
705 /* remove any full comment line */
706 for (i = 0; i < n; i++) {
707 if (list[i][0] == '#') {
708 for (j = i; j < n; j++)
709 list[j][0] = 0;
710 break;
711 }
712 }
713
714 /* cut any partial comment line */
715 for (i = 0; i < n; i++) {
716 if (strchr(list[i], '#'))
717 *strchr(list[i], '#') = 0;
718 }
719
720 /* strip any trailing blanks */
721 for (i = 0; i < n; i++) {
722 while (strlen(list[i]) > 0 && list[i][strlen(list[i])-1] == ' ')
723 list[i][strlen(list[i])-1] = 0;
724 }
725
726 /* check for variable assignment */
727 char eq[1024];
728 mstrlcpy(eq, lines[line], sizeof(eq));
729 if (strchr(eq, '#'))
730 *strchr(eq, '#') = 0;
731 for (i = 0, n = 0, nq = 0; i < (int)strlen(eq); i++) {
732 if (eq[i] == '\"')
733 nq = (nq == 0 ? 1 : 0);
734 if (eq[i] == '=' && nq == 0 &&
735 (i > 0 && (eq[i - 1] != '!') && eq[i - 1] != '<' && eq[i - 1] != '>'))
736 n++;
737 }
738 if (n == 1 && eq[0] != '=') {
739 // equation found
740 mstrlcpy(list[0], "SET", sizeof(list[0]));
741 p = eq;
742 while (*p == ' ')
743 p++;
744 mstrlcpy(list[1], p, sizeof(list[1]));
745 *strchr(list[1], '=') = 0;
746 if (strchr(list[1], ' '))
747 *strchr(list[1], ' ') = 0;
748 p = strchr(eq, '=')+1;
749 while (*p == ' ')
750 p++;
751 mstrlcpy(list[2], p, sizeof(list[2]));
752 while (strlen(list[2]) > 0 && list[2][strlen(list[2])-1] == ' ')
753 list[2][strlen(list[2])-1] = 0;
754 }
755
756 if (equal_ustring(list[0], "library")) {
757
758 } else if (equal_ustring(list[0], "include")) {
759 // if a path is given, use filename as entity reference
760 char *reference = strrchr(list[1], '/');
761 if (reference)
762 reference++;
763 else
764 reference = list[1];
765
766 xml += "&";
767 xml += reference;
768 xml += ";\n";
769
770 } else if (equal_ustring(list[0], "call")) {
771 xml += "<Call " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + ">";
772 for (i = 2; i < 100 && list[i][0]; i++) {
773 if (i > 2) {
774 xml += ",";
775 }
776 xml += list[i];
777 }
778 xml += "</Call>\n";
779
780 } else if (equal_ustring(list[0], "cat")) {
781 xml += "<Cat " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + ">";
782 for (i = 2; i < 100 && list[i][0]; i++) {
783 if (i > 2) {
784 xml += ",";
785 }
786 xml += q(list[i]);
787 }
788 xml += "</Cat>\n";
789
790 } else if (equal_ustring(list[0], "comment")) {
791 xml += "<Comment " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</Comment>\n";
792
793 } else if (equal_ustring(list[0], "exit")) {
794 xml += "<Exit " + qtoString(fullFilename, line + 1, level) + " />\n";
795
796 } else if (equal_ustring(list[0], "goto")) {
797 xml += "<Goto " + qtoString(fullFilename, line + 1, level) + " sline=" + q(list[1]) + " />\n";
798
799 } else if (equal_ustring(list[0], "if")) {
800 xml += "<If " + qtoString(fullFilename, line + 1, level) + " condition=\"";
801 for (i = 1; i < 100 && list[i][0] && stricmp(list[i], "THEN") != 0; i++) {
802 xml += list[i];
803 }
804 xml += "\">\n";
805
806 } else if (equal_ustring(list[0], "else")) {
807 xml += "<Else />\n";
808
809 } else if (equal_ustring(list[0], "endif")) {
810 xml += "</If>\n";
811
812 } else if (equal_ustring(list[0], "loop")) {
813 /* find end of loop */
814 for (i = line, nest = 0; i < n_lines; i++) {
815 strbreak(lines[i], list2, 100, ", ", FALSE);
816 if (equal_ustring(list2[0], "loop"))
817 nest++;
818 if (equal_ustring(list2[0], "endloop")) {
819 nest--;
820 if (nest == 0)
821 break;
822 }
823 }
824 if (i < n_lines)
825 endl = i + 1;
826 else
827 endl = line + 1;
828 if (list[2][0] == 0) {
829 xml += "<Loop " + qtoString(fullFilename, line + 1, level) + " le=\"" + std::to_string(endl) + "\" n=" + q(list[1]) + ">\n";
830 } else if (list[3][0] == 0) {
831 xml += "<Loop " + qtoString(fullFilename, line + 1, level) + " le=\"" + std::to_string(endl) + "\" var=" + q(list[1]) + " n=" +
832 q(list[2]) + ">\n";
833 } else {
834 xml += "<Loop " + qtoString(fullFilename, line + 1, level) + " le=\"" + std::to_string(endl) + "\" var=" + q(list[1]) + " values=\"";
835 for (i = 2; i < 100 && list[i][0]; i++) {
836 if (i > 2) {
837 xml += ",";
838 }
839 xml += list[i];
840 }
841 xml += "\">\n";
842 }
843 } else if (equal_ustring(list[0], "endloop")) {
844 xml += "</Loop>\n";
845
846 } else if (equal_ustring(list[0], "break")) {
847 xml += "<Break "+ qtoString(fullFilename, line + 1, level) +"></Break>\n";
848
849 } else if (equal_ustring(list[0], "message")) {
850 xml += "<Message " + qtoString(fullFilename, line + 1, level);
851 if (list[2][0] == '1')
852 xml += " wait=\"1\"";
853 xml += ">";
854 xml += list[1];
855 xml += "</Message>\n";
856
857 } else if (equal_ustring(list[0], "msg")) {
858 if (list[2][0]) {
859 xml += "<Msg " + qtoString(fullFilename, line + 1, level) + " type=\""+list[2]+"\">" + list[1] + "</Msg>\n";
860 } else {
861 xml += "<Msg " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</Msg>\n";
862 }
863
864 } else if (equal_ustring(list[0], "odbinc")) {
865 if (list[2][0] == 0)
866 mstrlcpy(list[2], "1", 2);
867 xml += "<ODBInc " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">" + list[2] + "</ODBInc>\n";
868
869 } else if (equal_ustring(list[0], "odbcreate")) {
870 if (list[3][0]) {
871 xml += "<ODBCreate " + qtoString(fullFilename, line + 1, level) + " size=" + q(list[3]) + " path=" + q(list[1]) + " type=" +
872 q(list[2]) + "></ODBCreate>\n";
873 } else {
874 xml += "<ODBCreate " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + " type=" + q(list[2]) +
875 "></ODBCreate>\n";
876 }
877
878 } else if (equal_ustring(list[0], "odbdelete")) {
879 xml += "<ODBDelete " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</ODBDelete>\n";
880
881 } else if (equal_ustring(list[0], "odbset")) {
882 if (list[3][0]) {
883 xml += "<ODBSet " + qtoString(fullFilename, line + 1, level) + " notify=" + q(list[3]) + " path=" + q(list[1]) + ">" +
884 list[2] + "</ODBSet>\n";
885 } else {
886 xml += "<ODBSet " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">" + list[2] + "</ODBSet>\n";
887 }
888
889 } else if (equal_ustring(list[0], "odbload")) {
890 if (list[2][0]) {
891 xml += "<ODBLoad " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[2]) + ">" + list[1] + "</ODBLoad>\n";
892 } else {
893 xml += "<ODBLoad " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</ODBLoad>\n";
894 }
895
896 } else if (equal_ustring(list[0], "odbget")) {
897 xml += "<ODBGet " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">" + list[2] + "</ODBGet>\n";
898
899 } else if (equal_ustring(list[0], "odblookup")) {
900 xml += "<ODBLookup " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) +
901 " string=" + q(list[2]) + ">" + list[3] + "</ODBLookup>\n";
902
903 } else if (equal_ustring(list[0], "odbsave")) {
904 xml += "<ODBSave " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">" + list[2] + "</ODBSave>\n";
905
906 } else if (equal_ustring(list[0], "odbsubdir")) {
907 if (list[2][0]) {
908 xml += "<ODBSubdir " + qtoString(fullFilename, line + 1, level) + " notify=" + q(list[2]) + " path=" + q(list[1]) + ">\n";
909 } else {
910 xml += "<ODBSubdir " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">\n";
911 }
912 } else if (equal_ustring(list[0], "endodbsubdir")) {
913 xml += "</ODBSubdir>\n";
914
915 } else if (equal_ustring(list[0], "param")) {
916 if (list[2][0] == 0 || equal_ustring(list[2], "bool")) { // name and bool
917 snprintf(error, error_size, "Parameter \"%s\" misses 'comment'", list[1]);
918 *error_line = line + 1;
919 return FALSE;
920
921 } else if (!list[4][0] && equal_ustring(list[3], "bool")) { // name, comment and bool
922 xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " type=\"bool\" " + " comment=" + q(list[2]) + "/>\n";
923 bool v = false;
924 c->odbs->RB((std::string("Param/Value/") + list[1]).c_str(), &v, true);
925 c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
926 } else if (!list[5][0] && equal_ustring(list[4], "bool")) { // name, comment, default and bool
927 xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " type=\"bool\" default=" + q(list[4]) +"/>\n";
928 std::string def(list[3]);
929 bool v(def == "1" || def == "true" || def == "TRUE");
930 c->odbs->RB((std::string("Param/Value/") + list[1]).c_str(), &v, true);
931 c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
932 c->odbs->WS((std::string("Param/Defaults/") + list[1]).c_str(), list[3]);
933
934 } else if (!list[3][0]) { // name and comment
935 xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " comment=" + q(list[2]) + " />\n";
936 std::string v;
937 c->odbs->RS((std::string("Param/Value/") + list[1]).c_str(), &v, true);
938 c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
939 } else if (!list[4][0]) { // name, comment and default
940 xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " comment=" + q(list[2]) + " default=" + q(list[3]) + " />\n";
941 std::string v(list[3]);
942 c->odbs->RS((std::string("Param/Value/") + list[1]).c_str(), &v, true);
943 c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
944 c->odbs->WS((std::string("Param/Defaults/") + list[1]).c_str(), list[3]);
945 // ss_sleep(1000); // use this for parameter test
946 } else {
947 xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " comment=" + q(list[2]) +
948 " options=\"";
949 std::string v;
950 c->odbs->RS((std::string("Param/Value/") + list[1]).c_str(), &v, true);
951 c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
952 std::vector<std::string> options;
953 for (i = 3; i < 100 && list[i][0]; i++) {
954 if (i > 3) {
955 xml += ",";
956 }
957 xml += list[i];
958 options.push_back(list[i]);
959 }
960 xml += "\" />\n";
961 c->odbs->WSA((std::string("Param/Options/") + list[1]).c_str(), options, 0);
962 }
963
964 // put back old parameter value if existing
965 try {
966 std::string ov = oldSeqParam[(const char *)list[1]];
967 c->odbs->WS((std::string("Param/Value/") + list[1]).c_str(), ov.c_str());
968 } catch(mexception &e) {}
969
970 } else if (equal_ustring(list[0], "rundescription")) {
971 xml += "<RunDescription " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</RunDescription>\n";
972
973 } else if (equal_ustring(list[0], "script")) {
974 if (list[2][0] == 0) {
975 xml += "<Script " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</Script>\n";
976 } else {
977 xml += "<Script " + qtoString(fullFilename, line + 1, level) + " params=\"";
978 for (i = 2; i < 100 && list[i][0]; i++) {
979 if (i > 2) {
980 xml += ",";
981 }
982 xml += list[i];
983 }
984 xml += "\">";
985 xml += list[1];
986 xml += "</Script>\n";
987 }
988
989 } else if (equal_ustring(list[0], "set")) {
990 xml += "<Set " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + ">" + list[2] + "</Set>\n";
991
992 } else if (equal_ustring(list[0], "subroutine")) {
993 xml += "\n<Subroutine " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + ">\n";
994
995 } else if (equal_ustring(list[0], "endsubroutine")) {
996 xml += "</Subroutine>\n";
997
998 } else if (equal_ustring(list[0], "transition")) {
999 xml += "<Transition " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</Transition>\n";
1000
1001 } else if (equal_ustring(list[0], "wait")) {
1002 if (!list[2][0]) {
1003 xml += "<Wait " + qtoString(fullFilename, line + 1, level) + " for=\"seconds\">" + list[1] + "</Wait>\n";
1004 } else if (!list[3][0]) {
1005 xml += "<Wait " + qtoString(fullFilename, line + 1, level) + " for=" + q(list[1]) + ">" + list[2] + "</Wait>\n";
1006 } else {
1007 xml += "<Wait " + qtoString(fullFilename, line + 1, level) + " for=" + q(list[1]) + " path=" + q(list[2]) + " op=" +
1008 q(list[3]);
1009
1010 // Optional timeout for ODBValue waits:
1011 // WAIT ODBValue, path, op, value, timeout, seconds
1012 // Store the timeout in the XML node. The runtime publishes progress
1013 // through State/Timeout value and State/Timeout limit.
1014 if (list[5][0]) {
1015 if (!equal_ustring(list[1], "ODBValue")) {
1016 snprintf(error, error_size, "WAIT timeout is only supported for WAIT ODBValue");
1017 *error_line = line + 1;
1018 return FALSE;
1019 }
1020 if (!equal_ustring(list[5], "timeout") || !list[6][0] || list[7][0]) {
1021 snprintf(error, error_size,
1022 "Invalid WAIT ODBValue timeout syntax. Use: WAIT ODBValue, path, op, value, timeout, seconds");
1023 *error_line = line + 1;
1024 return FALSE;
1025 }
1026 xml += " timeout=" + q(list[6]);
1027 }
1028
1029 xml += ">" + std::string(list[4]) + "</Wait>\n";
1030 }
1031
1032 } else if (list[0][0] == 0 || list[0][0] == '#') {
1033 /* skip empty or out-commented lines */
1034 } else {
1035 snprintf(error, error_size, "Invalid command \"%s\"", list[0]);
1036 *error_line = line + 1;
1037 return FALSE;
1038 }
1039 }
1040
1041 free(lines);
1042 free(buf);
1043 if (library) {
1044 xml += "\n</Library>\n";
1045 } else {
1046 xml += "</RunSequence>\n";
1047 }
1048
1049 // write XML to .xml file
1050 fprintf(fout, "%s", xml.c_str());
1051 fclose(fout);
1052
1053 return TRUE;
1054}
1055
1056/*------------------------------------------------------------------*/
1057
1059 int status;
1060 HNDLE hKey;
1061
1062 status = db_find_key(c->hDB, c->hSeq, "State", &hKey);
1063 if (status != DB_SUCCESS) {
1064 cm_msg(MERROR, "seq_read", "Cannot find /Sequencer/State in ODB, db_find_key() status %d", status);
1065 return;
1066 }
1067
1068 int size = sizeof(SEQUENCER);
1069 status = db_get_record1(c->hDB, hKey, seq, &size, 0, strcomb1(sequencer_str).c_str());
1070 if (status != DB_SUCCESS) {
1071 cm_msg(MERROR, "seq_read", "Cannot get /Sequencer/State from ODB, db_get_record1() status %d", status);
1072 return;
1073 }
1074}
1075
1076void seq_write(const SEQUENCER &seq, SeqCon* c) {
1077 int status;
1078 HNDLE hKey;
1079
1080 status = db_find_key(c->hDB, c->hSeq, "State", &hKey);
1081 if (status != DB_SUCCESS) {
1082 cm_msg(MERROR, "seq_write", "Cannot find /Sequencer/State in ODB, db_find_key() status %d", status);
1083 return;
1084 }
1085 status = db_set_record(c->hDB, hKey, (void *) &seq, sizeof(SEQUENCER), 0);
1086 if (status != DB_SUCCESS) {
1087 cm_msg(MERROR, "seq_write", "Cannot write to ODB /Sequencer/State, db_set_record() status %d", status);
1088 return;
1089 }
1090}
1091
1092/*------------------------------------------------------------------*/
1093
1095 seq.running = FALSE;
1096 seq.finished = FALSE;
1097 seq.paused = FALSE;
1099 seq.wait_limit = 0;
1100 seq.wait_value = 0;
1101 seq.timeout_limit = 0;
1102 seq.timeout_value = 0;
1103 seq.start_time = 0;
1104 seq.wait_type[0] = 0;
1105 seq.wait_odb[0] = 0;
1106 for (int i = 0; i < SEQ_NEST_LEVEL_LOOP; i++) {
1107 seq.loop_start_line[i] = 0;
1108 seq.sloop_start_line[i] = 0;
1109 seq.loop_end_line[i] = 0;
1110 seq.sloop_end_line[i] = 0;
1111 seq.loop_counter[i] = 0;
1112 seq.loop_n[i] = 0;
1113 }
1114 for (int i = 0; i < SEQ_NEST_LEVEL_IF; i++) {
1115 seq.if_else_line[i] = 0;
1116 seq.if_endif_line[i] = 0;
1117 }
1118 for (int i = 0; i < SEQ_NEST_LEVEL_SUB; i++) {
1119 seq.subroutine_end_line[i] = 0;
1120 seq.subroutine_return_line[i] = 0;
1121 seq.subroutine_call_line[i] = 0;
1122 seq.ssubroutine_call_line[i] = 0;
1123 seq.subroutine_param[i][0] = 0;
1124 }
1125 seq.current_line_number = 0;
1126 seq.scurrent_line_number = 0;
1127 seq.sfilename[0] = 0;
1128 seq.if_index = 0;
1129 seq.stack_index = 0;
1130 seq.error[0] = 0;
1131 seq.error_line = 0;
1132 seq.serror_line = 0;
1133 seq.subdir[0] = 0;
1134 seq.subdir_end_line = 0;
1135 seq.subdir_not_notify = 0;
1136 seq.message[0] = 0;
1137 seq.message_wait = FALSE;
1138 seq.stop_after_run = FALSE;
1139}
1140
1141/*------------------------------------------------------------------*/
1142
1143static void seq_start(SEQUENCER& seq, SeqCon* c, bool debug) {
1144 seq_read(&seq, c);
1145 seq_clear(seq);
1146
1147 // manage sequencer parameters and variables
1148
1149 midas::odb defaults(std::string("/") + c->odb_path + "/Param/Defaults");
1150 midas::odb param(std::string("/") + c->odb_path + "/Param/Value");
1151 midas::odb vars(std::string("/") + c->odb_path + "/Variables");
1152
1153 // check if /Param/Value is there
1154 for (midas::odb& d: defaults)
1155 if (!param.is_subkey(d.get_name())) {
1156 mstrlcpy(seq.error, "Cannot start script because /Sequencer/Param/Value is incomplete", sizeof(seq.error));
1157 seq_write(seq, c);
1158 cm_msg(MERROR, "sequencer", "Cannot start script because /Sequencer/Param/Value is incomplete");
1159 return;
1160 }
1161
1162 // copy /Sequencer/Param/Value to /Sequencer/Variables
1163 for (midas::odb& p: param)
1164 vars[p.get_name()] = p.s();
1165
1166 if (!c->pnseq) {
1167 mstrlcpy(seq.error, "Cannot start script, no script loaded", sizeof(seq.error));
1168 seq_write(seq, c);
1169 return;
1170 }
1171
1172 // start sequencer
1173 seq.running = TRUE;
1174 seq.debug = debug;
1175 seq.current_line_number = 1;
1176 seq.scurrent_line_number = 1;
1177 mstrlcpy(seq.sfilename, seq.filename, sizeof(seq.sfilename));
1178 seq_write(seq, c);
1179
1180 if (debug)
1181 cm_msg(MTALK, "sequencer", "Sequencer started with script \"%s\" in debugging mode.", seq.filename);
1182 else
1183 cm_msg(MTALK, "sequencer", "Sequencer started with script \"%s\".", seq.filename);
1184}
1185
1186/*------------------------------------------------------------------*/
1187
1188static void seq_stop(SEQUENCER& seq, SeqCon* c) {
1189 seq_read(&seq, c);
1190
1191 if (seq.follow_libraries) {
1192 std::string path(cm_get_path());
1193 path += "userfiles/sequencer/";
1194 path += seq.path;
1195 path += seq.filename;
1196
1197 loadMSL(c->odbs, path);
1198 }
1199
1200 seq_clear(seq);
1201 seq.finished = TRUE;
1202 seq_write(seq, c);
1203}
1204
1205/*------------------------------------------------------------------*/
1206
1208 seq_read(&seq, c);
1209 seq.stop_after_run = true;
1210 seq_write(seq, c);
1211}
1212
1213/*------------------------------------------------------------------*/
1214
1216 seq_read(&seq, c);
1217 seq.stop_after_run = false;
1218 seq_write(seq, c);
1219}
1220
1221/*------------------------------------------------------------------*/
1222
1223static void seq_pause(SEQUENCER& seq, SeqCon* c, bool flag) {
1224 seq_read(&seq, c);
1225 seq.paused = flag;
1226 if (!flag)
1227 seq.debug = false;
1228 seq_write(seq, c);
1229}
1230
1231/*------------------------------------------------------------------*/
1232
1234 seq_read(&seq, c);
1235
1236 // search ATEXIT subroutine
1237 for (int i = 1; i < mxml_get_line_number_end(mxml_find_node(c->pnseq, "RunSequence")); i++) {
1238 PMXML_NODE pt = mxml_get_node_at_line(c->pnseq, i);
1239 if (pt) {
1240 if (equal_ustring(mxml_get_name(pt), "Subroutine")) {
1241 if (equal_ustring(mxml_get_attribute(pt, "name"), "ATEXIT")) {
1242
1243 if (seq.stack_index == SEQ_NEST_LEVEL_SUB) {
1244 seq_error(seq, c, "Maximum subroutine level exceeded");
1245 return FALSE;
1246 }
1247
1248 // put routine end line on end stack
1249 seq.subroutine_end_line[seq.stack_index] = mxml_get_line_number_end(pt);
1250 seq.ssubroutine_call_line[seq.stack_index] = atoi(mxml_get_attribute(pt, "l"));
1251 seq.subroutine_return_line[seq.stack_index] = -1; // indicates exit on return
1252
1253 // put routine end line on end stack
1254 seq.subroutine_end_line[seq.stack_index] = mxml_get_line_number_end(pt);
1255
1256 // go to first line of subroutine
1257 seq.current_line_number = mxml_get_line_number_start(pt) + 1;
1258
1259 // increment stack
1260 seq.stack_index++;
1261
1262 seq_write(seq, c);
1263
1264 return TRUE;
1265 }
1266 }
1267 }
1268 }
1269
1270 // look for atexit subroutine
1271 return FALSE;
1272}
1273
1274/*------------------------------------------------------------------*/
1275
1276static void seq_step_over(SEQUENCER& seq, SeqCon* c) {
1277 seq_read(&seq, c);
1278 seq.paused = false;
1279 seq.debug = true;
1280 seq_write(seq, c);
1281}
1282
1283/*------------------------------------------------------------------*/
1284
1285static void seq_open_file(const char *str, SEQUENCER &seq, SeqCon* c) {
1286 seq.new_file = FALSE;
1287 seq.error[0] = 0;
1288 seq.error_line = 0;
1289 seq.serror_line = 0;
1290 if (c->pnseq) {
1291 mxml_free_tree(c->pnseq);
1292 c->pnseq = NULL;
1293 }
1294 c->odbs->WS("Script/Lines", "");
1295
1296 if (stristr(str, ".msl")) {
1297 int size = (int)strlen(str) + 1;
1298 char *xml_filename = (char *) malloc(size);
1299 mstrlcpy(xml_filename, str, size);
1300 strsubst(xml_filename, size, ".msl", ".xml");
1301 //printf("Parsing MSL sequencer file: %s to XML sequencer file %s\n", str, xml_filename);
1302
1303 std::string path(cm_get_path());
1304 path += "userfiles/sequencer/";
1305
1306 if (xml_filename[0] == '/') {
1307 path += xml_filename;
1308 path = path.substr(0, path.find_last_of('/') + 1);
1309 mstrlcpy(xml_filename, path.substr(path.find_last_of('/') + 1).c_str(), size);
1310 } else {
1311 path += seq.path;
1312 }
1313
1314 if (msl_parse(seq, c, 0, path.c_str(), str, xml_filename, seq.error, sizeof(seq.error), &seq.serror_line)) {
1315 //printf("Loading XML sequencer file: %s\n", xml_filename);
1316 std::string fn(path);
1317 if (!fn.empty() && fn.back() != '/')
1318 fn += '/';
1319 fn += xml_filename;
1320 c->pnseq = mxml_parse_file(fn.c_str(), seq.error, sizeof(seq.error), &seq.error_line);
1321 } else {
1322 //printf("Error in MSL sequencer file \"%s\" line %d, error: %s\n", str, seq.serror_line, seq.error);
1323 }
1324 free(xml_filename);
1325 } else {
1326 //printf("Loading XML sequencer file: %s\n", str);
1327 c->pnseq = mxml_parse_file(str, seq.error, sizeof(seq.error), &seq.error_line);
1328 }
1329}
1330
1331/*------------------------------------------------------------------*/
1332
1333static void seq_start_next(SEQUENCER &seq, SeqCon* c) {
1334 seq_read(&seq, c);
1335 if (seq.next_filename[0][0]) {
1336 mstrlcpy(seq.filename, seq.next_filename[0], sizeof(seq.filename));
1337 for (int i=0 ; i<9 ; i++)
1338 mstrlcpy(seq.next_filename[i], seq.next_filename[i+1], 256);
1339 seq.next_filename[9][0] = 0;
1340 seq_write(seq, c);
1341
1342 seq_open_file(seq.filename, seq, c);
1343
1344 seq_start(seq, c, false);
1345 }
1346}
1347
1348/*------------------------------------------------------------------*/
1349
1350static void seq_watch(HNDLE hDB, HNDLE hKeyChanged, int index, void *info) {
1351 char str[256];
1352 SeqCon* c = (SeqCon*)info;
1353
1354 seq_read(c->seqp, c);
1355
1356 if (c->seqp->new_file) {
1357 mstrlcpy(str, c->seqp->path, sizeof(str));
1358 if (strlen(str) > 1 && str[strlen(str) - 1] != DIR_SEPARATOR)
1359 mstrlcat(str, DIR_SEPARATOR_STR, sizeof(str));
1360 mstrlcat(str, c->seqp->filename, sizeof(str));
1361
1362 //printf("Load file %s\n", str);
1363
1364 seq_open_file(str, *(c->seqp), c);
1365
1366 seq_clear(*(c->seqp));
1367
1368 seq_write(*(c->seqp), c);
1369 }
1370}
1371
1372/*------------------------------------------------------------------*/
1373
1374static void seq_watch_command(HNDLE hDB, HNDLE hKeyChanged, int index, void *info) {
1375 SeqCon* c = (SeqCon*)info;
1376 SEQUENCER* seqp = c->seqp;
1377
1378 bool start_script = false;
1379 bool stop_immediately = false;
1380 bool stop_after_run = false;
1381 bool cancel_stop_after_run = false;
1382 bool pause_script = false;
1383 bool resume_script = false;
1384 bool debug_script = false;
1385 bool step_over = false;
1386 bool load_new_file = false;
1387
1388 c->odbs->RB("Command/Start script", &start_script);
1389 c->odbs->RB("Command/Stop immediately", &stop_immediately);
1390 c->odbs->RB("Command/Stop after run", &stop_after_run);
1391 c->odbs->RB("Command/Cancel stop after run", &cancel_stop_after_run);
1392 c->odbs->RB("Command/Pause script", &pause_script);
1393 c->odbs->RB("Command/Resume script", &resume_script);
1394 c->odbs->RB("Command/Debug script", &debug_script);
1395 c->odbs->RB("Command/Step over", &step_over);
1396 c->odbs->RB("Command/Load new file", &load_new_file);
1397
1398 if (load_new_file) {
1399 std::string filename;
1400 c->odbs->RS("State/Filename", &filename);
1401
1402 // printf("## load new file %s\n", filename.c_str());
1403
1404 if (filename.find("..") != std::string::npos) {
1405 mstrlcpy(seqp->error, "Cannot load \"", sizeof(seqp->error));
1406 mstrlcat(seqp->error, filename.c_str(), sizeof(seqp->error));
1407 mstrlcat(seqp->error, "\": file names with \"..\" is not permitted", sizeof(seqp->error));
1408 seq_write(*seqp, c);
1409 } else if (filename.find(".msl") == std::string::npos) {
1410 mstrlcpy(seqp->error, "Cannot load \"", sizeof(seqp->error));
1411 mstrlcat(seqp->error, filename.c_str(), sizeof(seqp->error));
1412 mstrlcat(seqp->error, "\": file name should end with \".msl\"", sizeof(seqp->error));
1413 seq_write(*seqp, c);
1414 } else {
1415 mstrlcpy(seqp->filename, filename.c_str(), sizeof(seqp->filename));
1416 std::string path = cm_expand_env(seqp->path);
1417 if (path.length() > 0 && path.back() != '/') {
1418 path += "/";
1419 }
1420 HNDLE hDB;
1422 seq_clear(*seqp);
1423 seq_open_file(filename.c_str(), *seqp, c);
1424 seq_write(*seqp, c);
1425 }
1426
1427 // indicate that we successfully load the new file
1428 c->odbs->WB("Command/Load new file", false);
1429
1430 // printf("## Set 'load new file' false\n");
1431 }
1432
1433 if (start_script) {
1434 c->odbs->WB("Command/Start script", false);
1435
1436 bool seq_running = false;
1437 c->odbs->RB("State/running", &seq_running);
1438
1439 if (!seq_running) {
1440 seq_start(*seqp, c, false);
1441 } else {
1442 cm_msg(MTALK, "sequencer", "Sequencer is already running");
1443 }
1444 }
1445
1446 if (stop_immediately && c->pnseq != NULL) {
1447 if (!goto_at_exit(*seqp, c)) {
1448 c->odbs->WB("Command/Stop immediately", false);
1449 seq_stop(*seqp, c);
1450 cm_msg(MTALK, "sequencer", "Sequencer is finished by \"stop immediately\".");
1451 } else {
1452 c->odbs->WB("Command/Stop immediately", false);
1453 cm_msg(MTALK, "sequencer", "Sequencer received \"stop immediately\". Executing ATEXIT.");
1454 }
1455 }
1456
1457 if (stop_after_run) {
1458 c->odbs->WB("Command/Stop after run", false);
1459 seq_stop_after_run(*seqp, c);
1460 }
1461
1462 if (cancel_stop_after_run) {
1463 c->odbs->WB("Command/Cancel stop after run", false);
1465 }
1466
1467 if (pause_script) {
1468 seq_pause(*seqp, c, true);
1469 c->odbs->WB("Command/Pause script", false);
1470 cm_msg(MTALK, "sequencer", "Sequencer is paused.");
1471 }
1472
1473 if (resume_script) {
1474 seq_pause(*seqp, c, false);
1475 c->odbs->WB("Command/Resume script", false);
1476 cm_msg(MTALK, "sequencer", "Sequencer is resumed.");
1477 }
1478
1479 if (debug_script) {
1480 c->odbs->WB("Command/Debug script", false);
1481
1482 bool seq_running = false;
1483 c->odbs->RB("State/running", &seq_running);
1484
1485 if (!seq_running) {
1486 seq_start(*seqp, c, true);
1487 } else {
1488 cm_msg(MTALK, "sequencer", "Sequencer is already running");
1489 }
1490 }
1491
1492 if (step_over) {
1493 seq_step_over(*seqp, c);
1494 c->odbs->WB("Command/Step over", false);
1495 }
1496}
1497
1498/*------------------------------------------------------------------*/
1499
1500//performs array index extraction including sequencer variables
1501void seq_array_index(SEQUENCER& seq, SeqCon* c, char *odbpath, int *index1, int *index2) {
1502 char str[256];
1503 *index1 = *index2 = 0;
1504 if (odbpath[strlen(odbpath) - 1] == ']') {
1505 if (strchr(odbpath, '[')) {
1506 //check for sequencer variables
1507 if (strchr((strchr(odbpath, '[') + 1), '$')) {
1508 mstrlcpy(str, strchr(odbpath, '[') + 1, sizeof(str));
1509 if (strchr(str, ']'))
1510 *strchr(str, ']') = 0;
1511 *index1 = atoi(eval_var(seq, c, str).c_str());
1512
1513 *strchr(odbpath, '[') = 0;
1514 } else {
1515 //standard expansion
1516 strarrayindex(odbpath, index1, index2);
1517 }
1518 }
1519 }
1520}
1521
1522/*------------------------------------------------------------------*/
1523
1524//set all matching keys to a value
1525int set_all_matching(HNDLE hDB, HNDLE hBaseKey, const char *odbpath, char *value, int index1, int index2, int notify) {
1526 int status, size;
1527 char data[256];
1528 KEY key;
1529
1530 std::vector<HNDLE> keys;
1531 status = db_find_keys(hDB, hBaseKey, odbpath, keys);
1532
1533 if (status != DB_SUCCESS)
1534 return status;
1535
1536 for (HNDLE hKey: keys) {
1537 db_get_key(hDB, hKey, &key);
1538 size = sizeof(data);
1539 db_sscanf(value, data, &size, 0, key.type);
1540
1541 /* extend data size for single string if necessary */
1542 if ((key.type == TID_STRING || key.type == TID_LINK)
1543 && (int) strlen(data) + 1 > key.item_size && key.num_values == 1)
1544 key.item_size = strlen(data) + 1;
1545
1546 if (key.num_values > 1 && index1 == -1) {
1547 for (int i = 0; i < key.num_values; i++)
1549 } else if (key.num_values > 1 && index2 > index1) {
1550 if (index1 < 0 || index1 >= key.num_values || index2 < 0 || index2 >= key.num_values)
1551 return DB_INVALID_PARAM;
1552 for (int i = index1; i < key.num_values && i <= index2; i++)
1554 } else {
1555 if (index1 < 0 || index1 >= key.num_values)
1556 return DB_INVALID_PARAM;
1557 status = db_set_data_index1(hDB, hKey, data, key.item_size, index1, key.type, notify);
1558 }
1559
1560 if (status != DB_SUCCESS) {
1561 return status;
1562 }
1563 }
1564
1565 return DB_SUCCESS;
1566}
1567
1568/*------------------------------------------------------------------*/
1569
1571 PMXML_NODE pn, pr, pt, pe;
1572 char odbpath[256], data[256], str[1024], name[32], op[32];
1573 std::string value;
1574 char list[100][XNAME_LENGTH];
1575 int i, j, l, n, status, size, index1, index2, state, run_number, cont;
1576 HNDLE hKey, hKeySeq;
1577 KEY key;
1578 double d;
1579 BOOL skip_step = FALSE;
1580
1581 if (!seq.running || seq.paused) {
1582 ss_sleep(10);
1583 return;
1584 }
1585
1586 if (c->pnseq == NULL) {
1587 seq_stop(seq, c);
1588 mstrlcpy(seq.error, "No script loaded", sizeof(seq.error));
1589 seq_write(seq, c);
1590 ss_sleep(10);
1591 return;
1592 }
1593
1594 db_find_key(c->hDB, c->hSeq, "State", &hKeySeq);
1595 if (!hKeySeq)
1596 return;
1597
1598 // Retrieve last midas message and put it into seq structure (later sent to ODB via db_set_record()
1599 cm_msg_retrieve(1, str, sizeof(str));
1600 str[19] = 0;
1601 strcpy(seq.last_msg, str+11);
1602
1603 pr = mxml_find_node(c->pnseq, "RunSequence");
1604 if (!pr) {
1605 seq_error(seq, c, "Cannot find &lt;RunSequence&gt; tag in XML file");
1606 return;
1607 }
1608
1609 int last_line = mxml_get_line_number_end(pr);
1610
1611 /* check for Subroutine end */
1612 if (seq.stack_index > 0 && seq.current_line_number == seq.subroutine_end_line[seq.stack_index - 1]) {
1613 seq_read(&seq, c);
1614 seq.subroutine_end_line[seq.stack_index - 1] = 0;
1615
1616 if (seq.subroutine_return_line[seq.stack_index - 1] == -1) {
1617 // end of atexit subroutine
1618 seq_stop(seq, c);
1619 cm_msg(MTALK, "sequencer", "Sequencer is finished.");
1620
1621 seq_start_next(seq, c);
1622 return;
1623 }
1624
1626 seq.subroutine_return_line[seq.stack_index - 1] = 0;
1627 seq.subroutine_call_line[seq.stack_index - 1] = 0;
1628 seq.ssubroutine_call_line[seq.stack_index - 1] = 0;
1629 seq.stack_index--;
1630 seq_write(seq, c);
1631 return;
1632 }
1633
1634 /* check for last line of script */
1635 if (seq.current_line_number > last_line) {
1636
1637 if (!goto_at_exit(seq, c)) {
1638 seq_stop(seq, c);
1639 cm_msg(MTALK, "sequencer", "Sequencer is finished.");
1640
1641 seq_start_next(seq, c);
1642 }
1643 return;
1644 }
1645
1646 /* check for loop end */
1647 for (i = SEQ_NEST_LEVEL_LOOP-1; i >= 0; i--)
1648 if (seq.loop_start_line[i] > 0)
1649 break;
1650 if (i >= 0) {
1651 if (seq.current_line_number == seq.loop_end_line[i]) {
1652 size = sizeof(seq);
1653 db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
1654
1655 if (seq.loop_counter[i] == seq.loop_n[i]) {
1656 seq.loop_counter[i] = 0;
1657 seq.loop_start_line[i] = 0;
1658 seq.sloop_start_line[i] = 0;
1659 seq.loop_end_line[i] = 0;
1660 seq.sloop_end_line[i] = 0;
1661 seq.loop_n[i] = 0;
1662 seq.current_line_number++;
1663 } else {
1664 pn = mxml_get_node_at_line(c->pnseq, seq.loop_start_line[i]);
1665 if (mxml_get_attribute(pn, "var")) {
1666 mstrlcpy(name, mxml_get_attribute(pn, "var"), sizeof(name));
1667 if (mxml_get_attribute(pn, "values")) {
1668 mstrlcpy(data, mxml_get_attribute(pn, "values"), sizeof(data));
1669 strbreak(data, list, 100, ",", FALSE);
1670 value = eval_var(seq, c, list[seq.loop_counter[i]]);
1671 } else if (mxml_get_attribute(pn, "n")) {
1672 value = std::to_string(seq.loop_counter[i] + 1);
1673 }
1674 sprintf(str, "Variables/%s", name); // FIXME: unsafe
1675 size = value.length() + 1;
1676 if (size < 32)
1677 size = 32;
1678 db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
1679 }
1680 seq.loop_counter[i]++;
1681 seq.current_line_number = seq.loop_start_line[i] + 1;
1682 }
1683 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
1684 return;
1685 }
1686 }
1687
1688 /* check for end of "if" statement */
1689 if (seq.if_index > 0 && seq.current_line_number == seq.if_endif_line[seq.if_index - 1]) {
1690 size = sizeof(seq);
1691 db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
1692 seq.if_index--;
1693 seq.if_line[seq.if_index] = 0;
1694 seq.if_else_line[seq.if_index] = 0;
1695 seq.if_endif_line[seq.if_index] = 0;
1696 seq.current_line_number++;
1697 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
1698 return;
1699 }
1700
1701 /* check for ODBSubdir end */
1702 if (seq.current_line_number == seq.subdir_end_line) {
1703 size = sizeof(seq);
1704 db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
1705 seq.subdir_end_line = 0;
1706 seq.subdir[0] = 0;
1708 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
1709 return;
1710 }
1711
1712 /* find node belonging to current line */
1713 pn = mxml_get_node_at_line(c->pnseq, seq.current_line_number);
1714 if (!pn) {
1715 size = sizeof(seq);
1716 db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
1717 seq.current_line_number++;
1718 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
1719 return;
1720 }
1721
1722 /* set MSL line from current element and put script into ODB if changed (library call) */
1723 pn = mxml_get_node_at_line(c->pnseq, seq.current_line_number);
1724 if (pn) {
1725 if (seq.follow_libraries) {
1726 if (mxml_get_attribute(pn, "l"))
1727 seq.scurrent_line_number = atoi(mxml_get_attribute(pn, "l"));
1728 if (mxml_get_attribute(pn, "fn")) {
1729 std::string filename = mxml_get_attribute(pn, "fn");
1730
1731 // load file into ODB if changed
1732 if (filename != std::string(seq.sfilename)) {
1733 if (loadMSL(c->odbs, filename))
1734 mstrlcpy(seq.sfilename, filename.c_str(), sizeof(seq.sfilename));
1735 }
1736 }
1737 } else {
1738 if (mxml_get_attribute(pn, "l") && (!mxml_get_attribute(pn, "lvl") || atoi(mxml_get_attribute(pn, "lvl")) == 0))
1739 seq.scurrent_line_number = atoi(mxml_get_attribute(pn, "l"));
1740 }
1741 }
1742
1743
1744 // out-comment following lines for debug output
1745#if 0
1746 if (seq.scurrent_line_number >= 0) {
1747 midas::odb o("/" + c->odb_path + "/Script/Lines");
1748 std::string s = o[seq.scurrent_line_number - 1];
1749 printf("%3d: %s\n", seq.scurrent_line_number, s.c_str());
1750 }
1751#endif
1752
1753 if (equal_ustring(mxml_get_name(pn), "PI") || equal_ustring(mxml_get_name(pn), "RunSequence") ||
1754 equal_ustring(mxml_get_name(pn), "Comment")) {
1755 // just skip
1756 seq.current_line_number++;
1757 skip_step = TRUE;
1758 }
1759
1760 /*---- ODBSubdir ----*/
1761 else if (equal_ustring(mxml_get_name(pn), "ODBSubdir")) {
1762 if (!mxml_get_attribute(pn, "path")) {
1763 seq_error(seq, c, "Missing attribute \"path\"");
1764 } else {
1765
1766 std::string s = mxml_get_attribute(pn, "path");
1767 if (s.find('$') != std::string::npos)
1768 s = eval_var(seq, c, s);
1769
1770 mstrlcpy(seq.subdir, s.c_str(), sizeof(seq.subdir));
1771 if (mxml_get_attribute(pn, "notify"))
1772 seq.subdir_not_notify = !atoi(mxml_get_attribute(pn, "notify"));
1773 seq.subdir_end_line = mxml_get_line_number_end(pn);
1774 seq.current_line_number++;
1775 }
1776 }
1777
1778 /*---- ODBSet ----*/
1779 else if (equal_ustring(mxml_get_name(pn), "ODBSet")) {
1780 if (!mxml_get_attribute(pn, "path")) {
1781 seq_error(seq, c, "Missing attribute \"path\"");
1782 } else {
1783 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
1784 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
1785 mstrlcat(odbpath, "/", sizeof(odbpath));
1786 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
1787
1788 if (strchr(odbpath, '$')) {
1789 if (strchr(odbpath, '[')) {
1790 // keep $ in index for later evaluation
1791 std::string s(odbpath);
1792 std::string s1 = s.substr(0, s.find('['));
1793 std::string s2 = s.substr(s.find('['));
1794 s1 = eval_var(seq, c, s1);
1795 s1 += s2;
1796 mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
1797 } else {
1798 // evaluate variable in path
1799 std::string s(odbpath);
1800 s = eval_var(seq, c, s);
1801 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
1802 }
1803 }
1804
1805 int notify = TRUE;
1806 if (seq.subdir_not_notify)
1807 notify = FALSE;
1808 if (mxml_get_attribute(pn, "notify"))
1809 notify = atoi(mxml_get_attribute(pn, "notify"));
1810
1811 index1 = index2 = 0;
1812 seq_array_index(seq, c, odbpath, &index1, &index2);
1813
1814 if (index1 < -1 || index2 < 0) {
1815 seq_error(seq, c, "Negative index not allowed");
1816 return;
1817 }
1818
1819 if (index1 > 1E6 || index2 > 1E6) {
1820 seq_error(seq, c, "Index too large for ODB");
1821 return;
1822 }
1823
1824 value = eval_var(seq, c, mxml_get_value(pn));
1825
1826 status = set_all_matching(c->hDB, 0, odbpath, (char *)value.c_str(), index1, index2, notify);
1827
1828 if (status == DB_SUCCESS) {
1829 size = sizeof(seq);
1830 db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
1831 seq.current_line_number++;
1832 } else if (status == DB_NO_KEY) {
1833 sprintf(str, "ODB key \"%s\" not found", odbpath); // FIXME: unsafe
1834 seq_error(seq, c, str);
1835 } else if (status == DB_INVALID_PARAM) {
1836 sprintf(str, "Invalid index %d for ODB key \"%s\"", index1, odbpath); // FIXME: unsafe
1837 seq_error(seq, c, str);
1838 } else {
1839 //something went really wrong
1840 sprintf(str, "Internal error %d", status);
1841 seq_error(seq, c, str);
1842 return;
1843 }
1844 }
1845 }
1846
1847 /*---- ODBLoad ----*/
1848 else if (equal_ustring(mxml_get_name(pn), "ODBLoad")) {
1849 if (mxml_get_value(pn)[0] == '/') {
1850
1851 // path relative to the one set in <exp>/userfiles/sequencer
1852 std::string path(cm_get_path());
1853 path += "userfiles/sequencer/";
1854 value = path;
1855 value += std::string(mxml_get_value(pn)+1);
1856 } else {
1857 // relative path to msl file
1858 std::string path(cm_get_path());
1859 path += "userfiles/sequencer/";
1860 path += seq.path;
1861 value = path;
1862 if (value.back() != '/')
1863 value += "/";
1864 value += mxml_get_value(pn);
1865 }
1866
1867 if (value.find('$') != std::string::npos)
1868 value = eval_var(seq, c, value);
1869
1870 // if path attribute is given
1871 if (mxml_get_attribute(pn, "path")) {
1872 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
1873 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
1874 mstrlcat(odbpath, "/", sizeof(odbpath));
1875 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
1876
1877 if (strchr(odbpath, '$')) {
1878 std::string s(odbpath);
1879 s = eval_var(seq, c, s);
1880 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
1881 }
1882
1883 // load at that key, if exists
1884 status = db_find_key(c->hDB, 0, odbpath, &hKey);
1885 if (status != DB_SUCCESS) {
1886 seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
1887 return;
1888 } else {
1889 status = db_load(c->hDB, hKey, value.c_str(), FALSE);
1890 }
1891 } else {
1892 // otherwise load at root
1893 status = db_load(c->hDB, 0, value.c_str(), FALSE);
1894 }
1895
1896 if (status == DB_SUCCESS) {
1897 size = sizeof(seq);
1898 db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
1899 seq.current_line_number++;
1900 } else if (status == DB_FILE_ERROR) {
1901 seq_error(seq, c, msprintf("Error reading file \"%s\"", value.c_str()).c_str());
1902 } else {
1903 //something went really wrong
1904 seq_error(seq, c, "Internal error loading ODB file!");
1905 return;
1906 }
1907 }
1908
1909 /*---- ODBSave ----*/
1910 else if (equal_ustring(mxml_get_name(pn), "ODBSave")) {
1911 if (mxml_get_value(pn)[0] == '/') {
1912
1913 // path relative to the one set in <exp>/userfiles/sequencer
1914 std::string path(cm_get_path());
1915 path += "userfiles/sequencer/";
1916 value = path;
1917 value += std::string(mxml_get_value(pn)+1);
1918 } else {
1919 // relative path to msl file
1920 std::string path(cm_get_path());
1921 path += "userfiles/sequencer/";
1922 path += seq.path;
1923 value = path;
1924 value += seq.filename;
1925 size_t pos = value.find_last_of('/');
1926 if (pos != std::string::npos)
1927 value = value.substr(0, pos);
1928 value += mxml_get_value(pn);
1929 }
1930
1931 if (value.find('$') != std::string::npos)
1932 value = eval_var(seq, c, value);
1933
1934 // if path attribute is given
1935 if (mxml_get_attribute(pn, "path") && *mxml_get_attribute(pn, "path")) {
1936 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
1937 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
1938 mstrlcat(odbpath, "/", sizeof(odbpath));
1939 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
1940
1941 if (strchr(odbpath, '$')) {
1942 std::string s(odbpath);
1943 s = eval_var(seq, c, s);
1944 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
1945 }
1946
1947 // find key or subdirectory to save
1948 status = db_find_key(c->hDB, 0, odbpath, &hKey);
1949 if (status != DB_SUCCESS) {
1950 seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
1951 return;
1952 } else {
1953 if (strstr(value.c_str(), ".json") || strstr(value.c_str(), ".JSON") || strstr(value.c_str(), ".js") || strstr(value.c_str(), ".JS"))
1955 else if (strstr(value.c_str(), ".xml") || strstr(value.c_str(), ".XML"))
1956 status = db_save_xml(c->hDB, hKey, value.c_str());
1957 else
1958 status = db_save(c->hDB, hKey, value.c_str(), FALSE);
1959 if (status != DB_SUCCESS) {
1960 seq_error(seq, c, msprintf("Cannot save file \"%s\", error %d", value.c_str(), status).c_str());
1961 return;
1962 }
1963 }
1964 } else {
1965 seq_error(seq, c, "No ODB path specified in ODBSAVE command");
1966 return;
1967 }
1968
1969 if (status == DB_SUCCESS) {
1970 size = sizeof(seq);
1971 db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
1972 seq.current_line_number++;
1973 } else if (status == DB_FILE_ERROR) {
1974 seq_error(seq, c, msprintf("Error reading file \"%s\"", value.c_str()).c_str());
1975 } else {
1976 //something went really wrong
1977 seq_error(seq, c, "Internal error loading ODB file!");
1978 return;
1979 }
1980 }
1981
1982 /*---- ODBGet ----*/
1983 else if (equal_ustring(mxml_get_name(pn), "ODBGet")) {
1984 if (!mxml_get_attribute(pn, "path")) {
1985 seq_error(seq, c, "Missing attribute \"path\"");
1986 } else {
1987 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
1988 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
1989 mstrlcat(odbpath, "/", sizeof(odbpath));
1990 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
1991
1992 if (strchr(odbpath, '$')) {
1993 if (strchr(odbpath, '[')) {
1994 // keep $ in index for later evaluation
1995 std::string s(odbpath);
1996 std::string s1 = s.substr(0, s.find('['));
1997 std::string s2 = s.substr(s.find('['));
1998 s1 = eval_var(seq, c, s1);
1999 s1 += s2;
2000 mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
2001 } else {
2002 // evaluate variable in path
2003 std::string s(odbpath);
2004 s = eval_var(seq, c, s);
2005 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2006 }
2007 }
2008
2009 /* check if index is supplied */
2010 index1 = index2 = 0;
2011 seq_array_index(seq, c, odbpath, &index1, &index2);
2012
2013 mstrlcpy(name, mxml_get_value(pn), sizeof(name));
2014 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2015 if (status != DB_SUCCESS) {
2016 seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
2017 return;
2018 } else {
2019 db_get_key(c->hDB, hKey, &key);
2020 size = sizeof(data);
2021
2022 status = db_get_data_index(c->hDB, hKey, data, &size, index1, key.type);
2023 if (key.type == TID_BOOL)
2024 value = *((int *) data) > 0 ? "1" : "0";
2025 else
2026 value = db_sprintf(data, size, 0, key.type);
2027
2028 sprintf(str, "Variables/%s", name); // FIXME: unsafe
2029 size = value.length() + 1;
2030 if (size < 32)
2031 size = 32;
2032 db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
2033
2034 size = sizeof(seq);
2035 db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
2036 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2037 }
2038 }
2039 }
2040
2041 /*---- ODBLookup ----*/
2042 else if (equal_ustring(mxml_get_name(pn), "ODBLookup")) {
2043 if (!mxml_get_attribute(pn, "path")) {
2044 seq_error(seq, c, "Missing attribute \"path\"");
2045 } else if (!mxml_get_attribute(pn, "string")) {
2046 seq_error(seq, c, "Missing attribute \"string\"");
2047 } else {
2048 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
2049 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
2050 mstrlcat(odbpath, "/", sizeof(odbpath));
2051 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
2052
2053 if (strchr(odbpath, '$')) {
2054 if (strchr(odbpath, '[')) {
2055 // keep $ in index for later evaluation
2056 std::string s(odbpath);
2057 std::string s1 = s.substr(0, s.find('['));
2058 std::string s2 = s.substr(s.find('['));
2059 s1 = eval_var(seq, c, s1);
2060 s1 += s2;
2061 mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
2062 } else {
2063 // evaluate variable in path
2064 std::string s(odbpath);
2065 s = eval_var(seq, c, s);
2066 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2067 }
2068 }
2069
2070 mstrlcpy(name, mxml_get_value(pn), sizeof(name));
2071
2072 auto s = eval_var(seq, c, mxml_get_attribute(pn, "string"));
2073 mstrlcpy(str, s.c_str(), sizeof(str));
2074
2075 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2076 if (status != DB_SUCCESS) {
2077 seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
2078 return;
2079 } else {
2080 db_get_key(c->hDB, hKey, &key);
2081
2082 // search for "str"
2083 for (i = 0 ; i<key.num_values ; i++) {
2084 size = sizeof(data);
2085 status = db_get_data_index(c->hDB, hKey, data, &size, i, key.type);
2086 if (strcmp((const char *)data, str) == 0)
2087 break;
2088 }
2089
2090 size = sizeof(i);
2091 if (i < key.num_values)
2092 value = db_sprintf(&i, size, 0, TID_INT);
2093 else {
2094 snprintf(str, sizeof(str), "\"ODBLOOKUP %s\" did not find string \"%s\"", odbpath, s.c_str());
2095 seq_error(seq, c, str);
2096 return;
2097 }
2098
2099 snprintf(str, sizeof(str), "Variables/%s", name);
2100 size = value.length() + 1;
2101 if (size < 32)
2102 size = 32;
2103 db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
2104
2105 size = sizeof(seq);
2106 db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
2107 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2108 }
2109 }
2110 }
2111
2112 /*---- ODBInc ----*/
2113 else if (equal_ustring(mxml_get_name(pn), "ODBInc")) {
2114 if (!mxml_get_attribute(pn, "path")) {
2115 seq_error(seq, c, "Missing attribute \"path\"");
2116 } else {
2117 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
2118 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
2119 mstrlcat(odbpath, "/", sizeof(odbpath));
2120 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
2121
2122 if (strchr(odbpath, '$')) {
2123 if (strchr(odbpath, '[')) {
2124 // keep $ in index for later evaluation
2125 std::string s(odbpath);
2126 std::string s1 = s.substr(0, s.find('['));
2127 std::string s2 = s.substr(s.find('['));
2128 s1 = eval_var(seq, c, s1);
2129 s1 += s2;
2130 mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
2131 } else {
2132 // evaluate variable in path
2133 std::string s(odbpath);
2134 s = eval_var(seq, c, s);
2135 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2136 }
2137 }
2138
2139 index1 = index2 = 0;
2140 seq_array_index(seq, c, odbpath, &index1, &index2);
2141
2142 value = eval_var(seq, c, mxml_get_value(pn));
2143
2144 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2145 if (status != DB_SUCCESS) {
2146 seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
2147 } else {
2148 db_get_key(c->hDB, hKey, &key);
2149 size = sizeof(data);
2150 db_get_data_index(c->hDB, hKey, data, &size, index1, key.type);
2151 std::string s = db_sprintf(data, size, 0, key.type);
2152 d = std::stof(s);
2153 d += std::stof(value);
2154 sprintf(str, "%lg", d);
2155 size = sizeof(data);
2156 db_sscanf(str, data, &size, 0, key.type);
2157
2158 int notify = TRUE;
2159 if (seq.subdir_not_notify)
2160 notify = FALSE;
2161 if (mxml_get_attribute(pn, "notify"))
2162 notify = atoi(mxml_get_attribute(pn, "notify"));
2163
2164 db_set_data_index1(c->hDB, hKey, data, key.item_size, index1, key.type, notify);
2165 seq.current_line_number++;
2166 }
2167 }
2168 }
2169
2170 /*---- ODBDelete ----*/
2171 else if (equal_ustring(mxml_get_name(pn), "ODBDelete")) {
2172 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
2173 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
2174 mstrlcat(odbpath, "/", sizeof(odbpath));
2175 mstrlcat(odbpath, mxml_get_value(pn), sizeof(odbpath));
2176
2177 if (strchr(odbpath, '$')) {
2178 std::string s(odbpath);
2179 s = eval_var(seq, c, s);
2180 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2181 }
2182
2183 status = db_delete(c->hDB, 0, odbpath);
2184 if (status != DB_SUCCESS) {
2185 seq_error(seq, c, msprintf("Cannot delete ODB key \"%s\"", odbpath).c_str());
2186 } else {
2187 seq.current_line_number++;
2188 }
2189 }
2190
2191 /*---- ODBCreate ----*/
2192 else if (equal_ustring(mxml_get_name(pn), "ODBCreate")) {
2193 if (!mxml_get_attribute(pn, "path")) {
2194 seq_error(seq, c, "Missing attribute \"path\"");
2195 } else if (!mxml_get_attribute(pn, "type")) {
2196 seq_error(seq, c, "Missing attribute \"type\"");
2197 } else {
2198 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
2199 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
2200 mstrlcat(odbpath, "/", sizeof(odbpath));
2201 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
2202
2203 if (strchr(odbpath, '$')) {
2204 std::string s(odbpath);
2205 s = eval_var(seq, c, s);
2206 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2207 }
2208
2209 /* get TID */
2210 unsigned int tid;
2211 for (tid = 0; tid < TID_LAST; tid++) {
2212 if (equal_ustring(rpc_tid_name(tid), mxml_get_attribute(pn, "type")))
2213 break;
2214 }
2215
2216 if (tid == TID_LAST)
2217 seq_error(seq, c, "Type must be one of UINT8,INT8,UINT16,INT16,UINT32,INT32,BOOL,FLOAT,DOUBLE,STRING");
2218 else {
2219
2220 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2221 if (status == DB_SUCCESS) {
2222 db_get_key(c->hDB, hKey, &key);
2223 if (key.type != tid) {
2224 db_delete_key(c->hDB, hKey);
2225 status = db_create_key(c->hDB, 0, odbpath, tid);
2226 }
2227 } else {
2228 status = db_create_key(c->hDB, 0, odbpath, tid);
2229 char dummy[32];
2230 memset(dummy, 0, sizeof(dummy));
2231 if (tid == TID_STRING || tid == TID_LINK)
2232 db_set_value(c->hDB, 0, odbpath, dummy, 32, 1, tid);
2233 }
2234
2235 if (status != DB_SUCCESS && status != DB_CREATED) {
2236 seq_error(seq, c, msprintf("Cannot create ODB key \"%s\", error code %d", odbpath, status).c_str());
2237 } else {
2238 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2239 if (mxml_get_attribute(pn, "size")) {
2240 i = atoi(eval_var(seq, c, mxml_get_attribute(pn, "size")).c_str());
2241 if (i > 1)
2242 db_set_num_values(c->hDB, hKey, i);
2243 }
2244 seq.current_line_number++;
2245 }
2246 }
2247 }
2248 }
2249
2250 /*---- RunDescription ----*/
2251 else if (equal_ustring(mxml_get_name(pn), "RunDescription")) {
2252 db_set_value(c->hDB, 0, "/Experiment/Run Parameters/Run Description", mxml_get_value(pn), 256, 1, TID_STRING);
2253 seq.current_line_number++;
2254 }
2255
2256 /*---- Script ----*/
2257 else if (equal_ustring(mxml_get_name(pn), "Script")) {
2258 sprintf(str, "%s", mxml_get_value(pn)); // FIXME: unsafe
2259
2260 if (mxml_get_attribute(pn, "params")) {
2261 mstrlcpy(data, mxml_get_attribute(pn, "params"), sizeof(data));
2262 n = strbreak(data, list, 100, ",", FALSE);
2263 for (i = 0; i < n; i++) {
2264
2265 value = eval_var(seq, c, list[i]);
2266
2267 mstrlcat(str, " ", sizeof(str));
2268 mstrlcat(str, value.c_str(), sizeof(str));
2269 }
2270 }
2271
2272 std::string s(str);
2274 std::string r = ss_execs(s.c_str());
2275
2276 // put result into SCRIPT_RESULT variable
2277 db_set_value(c->hDB, c->hSeq, "Variables/SCRIPT_RESULT", r.c_str(), r.length(), 1, TID_STRING);
2278
2279 seq.current_line_number++;
2280 }
2281
2282 /*---- Transition ----*/
2283 else if (equal_ustring(mxml_get_name(pn), "Transition")) {
2284 if (equal_ustring(mxml_get_value(pn), "Start")) {
2285 if (!seq.transition_request) {
2287 size = sizeof(state);
2288 db_get_value(c->hDB, 0, "/Runinfo/State", &state, &size, TID_INT32, FALSE);
2289 if (state != STATE_RUNNING) {
2290 size = sizeof(run_number);
2291 db_get_value(c->hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT32, FALSE);
2293 if (status != CM_SUCCESS) {
2294 seq_error(seq, c, msprintf("Cannot start run: %s", str).c_str());
2295 }
2296 }
2297 } else {
2298 // Wait until transition has finished
2299 size = sizeof(state);
2300 db_get_value(c->hDB, 0, "/Runinfo/State", &state, &size, TID_INT32, FALSE);
2301 if (state == STATE_RUNNING) {
2303 seq.current_line_number++;
2304 }
2305 }
2306 } else if (equal_ustring(mxml_get_value(pn), "Stop")) {
2307 if (!seq.transition_request) {
2309 size = sizeof(state);
2310 db_get_value(c->hDB, 0, "/Runinfo/State", &state, &size, TID_INT32, FALSE);
2311 if (state != STATE_STOPPED) {
2314 // do nothing
2315 } else if (status != CM_SUCCESS) {
2316 seq_error(seq, c, msprintf("Cannot stop run: %s", str).c_str());
2317 }
2318 }
2319 } else {
2320 // Wait until transition has finished
2321 size = sizeof(state);
2322 db_get_value(c->hDB, 0, "/Runinfo/State", &state, &size, TID_INT32, FALSE);
2323 if (state == STATE_STOPPED) {
2324 size = sizeof(seq);
2325 db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
2326
2328
2329 if (seq.stop_after_run) {
2330 seq.stop_after_run = FALSE;
2331
2332 if (!goto_at_exit(seq, c)) {
2333 seq.running = FALSE;
2334 seq.finished = TRUE;
2335 seq_stop(seq, c);
2336 cm_msg(MTALK, "sequencer", "Sequencer is finished by \"stop after current run\".");
2337 } else {
2338 cm_msg(MTALK, "sequencer", "Sequencer is going to finish by \"stop after current run\". Executing ATEXIT.");
2339 }
2340 } else {
2341 seq.current_line_number++;
2342 }
2343
2344 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
2345 }
2346 }
2347 } else {
2348 seq_error(seq, c, msprintf("Invalid transition \"%s\"", mxml_get_value(pn)).c_str());
2349 return;
2350 }
2351 }
2352
2353 /*---- Wait ----*/
2354 else if (equal_ustring(mxml_get_name(pn), "Wait")) {
2355 if (equal_ustring(mxml_get_attribute(pn, "for"), "Events")) {
2356 n = atoi(eval_var(seq, c, mxml_get_value(pn)).c_str());
2357 seq.wait_limit = (float) n;
2358 strcpy(seq.wait_type, "Events");
2359 seq.timeout_limit = 0;
2360 seq.timeout_value = 0;
2361 size = sizeof(d);
2362 db_get_value(c->hDB, 0, "/Equipment/Trigger/Statistics/Events sent", &d, &size, TID_DOUBLE, FALSE);
2363 seq.wait_value = (float) d;
2364 if (d >= n) {
2365 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2366 seq.wait_limit = 0;
2367 seq.wait_value = 0;
2368 seq.timeout_limit = 0;
2369 seq.timeout_value = 0;
2370 seq.wait_type[0] = 0;
2371 seq.wait_odb[0] = 0;
2372 }
2373 seq.wait_value = (float) d;
2374 } else if (equal_ustring(mxml_get_attribute(pn, "for"), "ODBValue")) {
2375 seq.wait_limit = (float) atof(eval_var(seq, c, mxml_get_value(pn)).c_str());
2376 mstrlcpy(seq.wait_type, "ODB", sizeof(seq.wait_type));
2377 if (!mxml_get_attribute(pn, "path")) {
2378 seq_error(seq, c, "\"path\" must be given for ODB values");
2379 return;
2380 } else {
2381 mstrlcpy(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
2382 mstrlcpy(seq.wait_odb, odbpath, sizeof(seq.wait_odb));
2383
2384 if (strchr(odbpath, '$')) {
2385 if (strchr(odbpath, '[')) {
2386 // keep $ in index for later evaluation
2387 std::string s(odbpath);
2388 std::string s1 = s.substr(0, s.find('['));
2389 std::string s2 = s.substr(s.find('['));
2390 s1 = eval_var(seq, c, s1);
2391 s1 += s2;
2392 mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
2393 } else {
2394 // evaluate variable in path
2395 std::string s(odbpath);
2396 s = eval_var(seq, c, s);
2397 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2398 }
2399 }
2400
2401 index1 = index2 = 0;
2402 seq_array_index(seq, c, odbpath, &index1, &index2);
2403 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2404 if (status != DB_SUCCESS) {
2405 seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
2406 return;
2407 } else {
2408 if (mxml_get_attribute(pn, "op"))
2409 mstrlcpy(op, mxml_get_attribute(pn, "op"), sizeof(op));
2410 else
2411 strcpy(op, "!=");
2412 mstrlcat(seq.wait_type, op, sizeof(seq.wait_type));
2413
2414 db_get_key(c->hDB, hKey, &key);
2415 size = sizeof(data);
2416 db_get_data_index(c->hDB, hKey, data, &size, index1, key.type);
2417 if (key.type == TID_BOOL)
2418 value = *((int *) data) > 0 ? "1" : "0";
2419 else
2420 value = db_sprintf(data, size, 0, key.type);
2421 cont = FALSE;
2422 seq.wait_value = std::stof(value);
2423 if (equal_ustring(op, ">=")) {
2424 cont = (seq.wait_value >= seq.wait_limit);
2425 } else if (equal_ustring(op, ">")) {
2426 cont = (seq.wait_value > seq.wait_limit);
2427 } else if (equal_ustring(op, "<=")) {
2428 cont = (seq.wait_value <= seq.wait_limit);
2429 } else if (equal_ustring(op, "<")) {
2430 cont = (seq.wait_value < seq.wait_limit);
2431 } else if (equal_ustring(op, "==")) {
2432 cont = (seq.wait_value == seq.wait_limit);
2433 } else if (equal_ustring(op, "!=")) {
2434 cont = (seq.wait_value != seq.wait_limit);
2435 } else {
2436 seq_error(seq, c, msprintf("Invalid comaprison \"%s\"", op).c_str());
2437 return;
2438 }
2439
2440 // Keep the existing ODB wait display semantics:
2441 // wait_limit = requested ODB target value
2442 // wait_value = current ODB value
2443 // Publish the optional timeout separately so the web interface can
2444 // display the ODB condition and a normal progress bar at the same time.
2445 bool timed_out = false;
2446 const char *timeout_attr = mxml_get_attribute(pn, "timeout");
2447 if (timeout_attr && timeout_attr[0]) {
2448 float timeout_sec = (float) atof(eval_var(seq, c, timeout_attr).c_str());
2449 if (timeout_sec < 0) {
2450 seq_error(seq, c, msprintf("Invalid WAIT ODBValue timeout \"%s\"", timeout_attr).c_str());
2451 return;
2452 }
2453
2454 // Use the same monotonic millisecond clock base as WAIT Seconds.
2455 if (seq.start_time == 0)
2456 seq.start_time = ss_millitime();
2457
2458 DWORD elapsed_ms = ss_millitime() - seq.start_time;
2459
2460 seq.timeout_limit = timeout_sec;
2461 seq.timeout_value = (float) elapsed_ms / 1000.0f;
2462 if (seq.timeout_value > seq.timeout_limit)
2463 seq.timeout_value = seq.timeout_limit;
2464
2465 timed_out = elapsed_ms > (DWORD) (1000.0f * timeout_sec);
2466 } else {
2467 seq.timeout_limit = 0;
2468 seq.timeout_value = 0;
2469 }
2470
2471 if (cont || timed_out) {
2472 if (timed_out && !cont) {
2473 cm_msg(MINFO, "sequencer", "WAIT ODBValue timeout after %.1f seconds: %s %s %g not satisfied",
2474 seq.timeout_limit, odbpath, op, seq.wait_limit);
2475 }
2476 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2477 seq.start_time = 0;
2478 seq.wait_limit = 0;
2479 seq.wait_value = 0;
2480 seq.timeout_limit = 0;
2481 seq.timeout_value = 0;
2482 seq.wait_type[0] = 0;
2483 seq.wait_odb[0] = 0;
2484 }
2485 }
2486 }
2487 } else if (equal_ustring(mxml_get_attribute(pn, "for"), "Seconds")) {
2488 seq.wait_limit = 1000.0f * (float) atof(eval_var(seq, c, mxml_get_value(pn)).c_str());
2489 strcpy(seq.wait_type, "Seconds");
2490 seq.timeout_limit = 0;
2491 seq.timeout_value = 0;
2492 if (seq.start_time == 0) {
2493 seq.start_time = ss_millitime();
2494 seq.wait_value = 0;
2495 } else {
2496 seq.wait_value = (float) (ss_millitime() - seq.start_time);
2497 if (seq.wait_value > seq.wait_limit)
2498 seq.wait_value = seq.wait_limit;
2499 }
2500 if (ss_millitime() - seq.start_time > (DWORD) seq.wait_limit) {
2501 seq.current_line_number++;
2502 seq.start_time = 0;
2503 seq.wait_limit = 0;
2504 seq.wait_value = 0;
2505 seq.timeout_limit = 0;
2506 seq.timeout_value = 0;
2507 seq.wait_type[0] = 0;
2508 seq.wait_odb[0] = 0;
2509 }
2510 } else {
2511 seq_error(seq, c, msprintf("Invalid wait attribute \"%s\"", mxml_get_attribute(pn, "for")).c_str());
2512 }
2513
2514 // sleep to keep the CPU from consuming 100%
2515 ss_sleep(1);
2516 }
2517
2518 /*---- Loop start ----*/
2519 else if (equal_ustring(mxml_get_name(pn), "Loop")) {
2520 for (i = 0; i < SEQ_NEST_LEVEL_LOOP; i++)
2521 if (seq.loop_start_line[i] == 0)
2522 break;
2523 if (i == SEQ_NEST_LEVEL_LOOP) {
2524 seq_error(seq, c, "Maximum loop nesting exceeded");
2525 return;
2526 }
2528 seq.loop_end_line[i] = mxml_get_line_number_end(pn);
2529 if (mxml_get_attribute(pn, "l"))
2530 seq.sloop_start_line[i] = atoi(mxml_get_attribute(pn, "l"));
2531 if (mxml_get_attribute(pn, "le"))
2532 seq.sloop_end_line[i] = atoi(mxml_get_attribute(pn, "le"));
2533 seq.loop_counter[i] = 1;
2534
2535 if (mxml_get_attribute(pn, "n")) {
2536 if (equal_ustring(mxml_get_attribute(pn, "n"), "infinite"))
2537 seq.loop_n[i] = -1;
2538 else {
2539 seq.loop_n[i] = atoi(eval_var(seq, c, mxml_get_attribute(pn, "n")).c_str());
2540 }
2541 value = "1";
2542 } else if (mxml_get_attribute(pn, "values")) {
2543 mstrlcpy(data, mxml_get_attribute(pn, "values"), sizeof(data));
2544 seq.loop_n[i] = strbreak(data, list, 100, ",", FALSE);
2545 value = eval_var(seq, c, list[0]);
2546 } else {
2547 seq_error(seq, c, "Missing \"var\" or \"n\" attribute");
2548 return;
2549 }
2550
2551 if (mxml_get_attribute(pn, "var")) {
2552 mstrlcpy(name, mxml_get_attribute(pn, "var"), sizeof(name));
2553 sprintf(str, "Variables/%s", name); // FIXME: unsafe
2554 size = value.length() + 1;
2555 if (size < 32)
2556 size = 32;
2557 db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
2558 }
2559
2560 seq.current_line_number++;
2561 }
2562
2563 /*---- Break ----*/
2564 else if (equal_ustring(mxml_get_name(pn), "Break")) {
2565
2566 // finish current if statement
2567 while (seq.if_index > 0 &&
2568 seq.current_line_number > seq.if_line[seq.if_index - 1] &&
2569 seq.current_line_number < seq.if_endif_line[seq.if_index-1]) {
2570 size = sizeof(seq);
2571 db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
2572 seq.if_index--;
2573 seq.if_line[seq.if_index] = 0;
2574 seq.if_else_line[seq.if_index] = 0;
2575 seq.if_endif_line[seq.if_index] = 0;
2576 seq.current_line_number++;
2577 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
2578 }
2579
2580 // goto next loop end
2581 for (i = 0; i < SEQ_NEST_LEVEL_LOOP; i++)
2582 if (seq.loop_start_line[i] == 0)
2583 break;
2584 if (i == 0) {
2585 seq_error(seq, c, "\"Break\" outside any loop");
2586 return;
2587 }
2588
2589 // force end of loop in next check
2590 seq.current_line_number = seq.loop_end_line[i-1];
2591 seq.loop_counter[i-1] = seq.loop_n[i-1];
2592 }
2593
2594 /*---- If ----*/
2595 else if (equal_ustring(mxml_get_name(pn), "If")) {
2596
2597 if (seq.if_index == SEQ_NEST_LEVEL_IF) {
2598 seq_error(seq, c, "Maximum number of nested if..endif exceeded");
2599 return;
2600 }
2601
2602 // store "if" and "endif" lines
2603 seq.if_line[seq.if_index] = seq.current_line_number;
2604 seq.if_endif_line[seq.if_index] = mxml_get_line_number_end(pn);
2605
2606 // search for "else" line
2607 seq.if_else_line[seq.if_index] = 0;
2608 for (j = seq.current_line_number + 1; j < mxml_get_line_number_end(pn) + 1; j++) {
2609 pe = mxml_get_node_at_line(c->pnseq, j);
2610
2611 // skip nested if..endif
2612 if (pe && equal_ustring(mxml_get_name(pe), "If")) {
2613 j = mxml_get_line_number_end(pe);
2614 continue;
2615 }
2616
2617 // store "else" if found
2618 if (pe && equal_ustring(mxml_get_name(pe), "Else")) {
2619 seq.if_else_line[seq.if_index] = j;
2620 break;
2621 }
2622 }
2623
2624 mstrlcpy(str, mxml_get_attribute(pn, "condition"), sizeof(str));
2625 i = eval_condition(seq, c, str);
2626 if (i < 0) {
2627 seq_error(seq, c, "Invalid number in comparison");
2628 return;
2629 }
2630
2631 if (i == 1)
2632 seq.current_line_number++;
2633 else if (seq.if_else_line[seq.if_index])
2634 seq.current_line_number = seq.if_else_line[seq.if_index] + 1;
2635 else
2637
2638 seq.if_index++;
2639 }
2640
2641 /*---- Else ----*/
2642 else if (equal_ustring(mxml_get_name(pn), "Else")) {
2643 // goto next "Endif"
2644 if (seq.if_index == 0) {
2645 seq_error(seq, c, "Unexpected Else");
2646 return;
2647 }
2648 seq.current_line_number = seq.if_endif_line[seq.if_index - 1];
2649 }
2650
2651 /*---- Exit ----*/
2652 else if (equal_ustring(mxml_get_name(pn), "Exit")) {
2653 seq_stop(seq, c);
2654 cm_msg(MTALK, "sequencer", "Sequencer is finished.");
2655 return;
2656 }
2657
2658 /*---- Goto ----*/
2659 else if (equal_ustring(mxml_get_name(pn), "Goto")) {
2660 if (!mxml_get_attribute(pn, "line") && !mxml_get_attribute(pn, "sline")) {
2661 seq_error(seq, c, "Missing line number");
2662 return;
2663 }
2664 if (mxml_get_attribute(pn, "line")) {
2665 seq.current_line_number = atoi(eval_var(seq, c, mxml_get_attribute(pn, "line")).c_str());
2666 }
2667 if (mxml_get_attribute(pn, "sline")) {
2668 mstrlcpy(str, eval_var(seq, c, mxml_get_attribute(pn, "sline")).c_str(), sizeof(str));
2669 for (i = 0; i < last_line; i++) {
2670 pt = mxml_get_node_at_line(c->pnseq, i);
2671 if (pt && mxml_get_attribute(pt, "l")) {
2672 l = atoi(mxml_get_attribute(pt, "l"));
2673 if (atoi(str) == l) {
2674 seq.current_line_number = i;
2675 break;
2676 }
2677 }
2678 }
2679 }
2680 }
2681
2682 /*---- Library ----*/
2683 else if (equal_ustring(mxml_get_name(pn), "Library")) {
2684 // simply skip libraries
2685 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2686 }
2687
2688 /*---- Subroutine ----*/
2689 else if (equal_ustring(mxml_get_name(pn), "Subroutine")) {
2690 // simply skip subroutines
2691 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2692 }
2693
2694 /*---- Param ----*/
2695 else if (equal_ustring(mxml_get_name(pn), "Param")) {
2696 // simply skip parameters
2697 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2698 }
2699
2700 /*---- Set ----*/
2701 else if (equal_ustring(mxml_get_name(pn), "Set")) {
2702 if (!mxml_get_attribute(pn, "name")) {
2703 seq_error(seq, c, "Missing variable name");
2704 return;
2705 }
2706 mstrlcpy(name, mxml_get_attribute(pn, "name"), sizeof(name));
2707 value = eval_var(seq, c, mxml_get_value(pn));
2708
2709 if (strchr(name, '[')) {
2710 // array
2711 mstrlcpy(str, strchr(name, '[')+1, sizeof(str));
2712 if (strchr(str, ']'))
2713 *strchr(str, ']') = 0;
2714 int index = atoi(eval_var(seq, c, str).c_str());
2715 *strchr(name, '[') = 0;
2716 sprintf(str, "Variables/%s", name); // FIXME: unsafe
2717 status = db_find_key(c->hDB, c->hSeq, str, &hKey);
2718 if (status != DB_SUCCESS) {
2719 db_create_key(c->hDB, c->hSeq, str, TID_STRING);
2720 status = db_find_key(c->hDB, c->hSeq, str, &hKey);
2721 }
2722 size = value.length() + 1;
2723 if (size < 32)
2724 size = 32;
2725 status = db_set_data_index(c->hDB, hKey, value.c_str(), size, index, TID_STRING);
2726 } else {
2727 sprintf(str, "Variables/%s", name); // FIXME: unsafe
2728 size = value.length() + 1;
2729 if (size < 32)
2730 size = 32;
2731 db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
2732 }
2733
2734 // check if variable is used in loop
2735 for (i = SEQ_NEST_LEVEL_LOOP-1; i >= 0; i--)
2736 if (seq.loop_start_line[i] > 0) {
2737 pr = mxml_get_node_at_line(c->pnseq, seq.loop_start_line[i]);
2738 if (mxml_get_attribute(pr, "var")) {
2739 if (equal_ustring(mxml_get_attribute(pr, "var"), name))
2740 seq.loop_counter[i] = atoi(value.c_str());
2741 }
2742 }
2743
2744 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2745 }
2746
2747 /*---- Message ----*/
2748 else if (equal_ustring(mxml_get_name(pn), "Message")) {
2749 if (strchr(mxml_get_value(pn), '$')) // evaluate message string if $ present
2750 value = eval_var(seq, c, mxml_get_value(pn));
2751 else
2752 value = mxml_get_value(pn); // treat message as string
2753 const char *wait_attr = mxml_get_attribute(pn, "wait");
2754 bool wait = false;
2755 if (wait_attr)
2756 wait = (atoi(wait_attr) == 1);
2757
2758 if (!wait) {
2759 // message with no wait: set seq.message and move on. we do not care if web page clears it
2760 mstrlcpy(seq.message, value.c_str(), sizeof(seq.message));
2761 seq.message_wait = FALSE;
2762 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
2763 } else {
2764 // message with wait
2765
2766 // if message_wait not set, we are here for the first time
2767 if (!seq.message_wait) {
2768 mstrlcpy(seq.message, value.c_str(), sizeof(seq.message));
2769 seq.message_wait = TRUE;
2770 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
2771 // wait
2772 return;
2773 } else {
2774 // message_wait is set, we have been here before
2775
2776 // if web page did not clear the message, keep waiting
2777 if (seq.message[0] != 0) {
2778 // wait
2779 return;
2780 }
2781
2782 // web page cleared the message, we are done with waiting
2783 seq.message_wait = false;
2784 }
2785 }
2786
2787 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2788 }
2789
2790 /*---- Msg ----*/
2791 else if (equal_ustring(mxml_get_name(pn), "Msg")) {
2792 if (strchr(mxml_get_value(pn), '$')) // evaluate message string if $ present
2793 value = eval_var(seq, c, mxml_get_value(pn));
2794 else
2795 value = mxml_get_value(pn); // treat message as string
2796 std::string type = "INFO";
2797 if (mxml_get_attribute(pn, "type"))
2798 type = std::string(mxml_get_attribute(pn, "type"));
2799
2800 if (type == "ERROR")
2801 cm_msg(MERROR, "sequencer", "%s", value.c_str());
2802 else if (type == "DEBUG")
2803 cm_msg(MDEBUG, "sequencer", "%s", value.c_str());
2804 else if (type == "LOG")
2805 cm_msg(MLOG, "sequencer", "%s", value.c_str());
2806 else if (type == "TALK")
2807 cm_msg(MTALK, "sequencer", "%s", value.c_str());
2808 else
2809 cm_msg(MINFO, "sequencer", "%s", value.c_str());
2810
2811 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2812 }
2813
2814 /*---- Cat ----*/
2815 else if (equal_ustring(mxml_get_name(pn), "Cat")) {
2816 if (!mxml_get_attribute(pn, "name")) {
2817 seq_error(seq, c, "Missing variable name");
2818 return;
2819 }
2820 mstrlcpy(name, mxml_get_attribute(pn, "name"), sizeof(name));
2821 if (!concatenate(seq, c, data, sizeof(data), mxml_get_value(pn)))
2822 return;
2823 sprintf(str, "Variables/%s", name); // FIXME: unsafe
2824 size = strlen(data) + 1;
2825 if (size < 32)
2826 size = 32;
2827 db_set_value(c->hDB, c->hSeq, str, data, size, 1, TID_STRING);
2828
2829 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2830 }
2831
2832 /*---- Call ----*/
2833 else if (equal_ustring(mxml_get_name(pn), "Call")) {
2834 if (seq.stack_index == SEQ_NEST_LEVEL_SUB) {
2835 seq_error(seq, c, "Maximum subroutine level exceeded");
2836 return;
2837 } else {
2838 // put current line number on stack
2839 seq.subroutine_call_line[seq.stack_index] = mxml_get_line_number_end(pn);
2840 seq.ssubroutine_call_line[seq.stack_index] = atoi(mxml_get_attribute(pn, "l"));
2841 seq.subroutine_return_line[seq.stack_index] = mxml_get_line_number_end(pn) + 1;
2842
2843 // search subroutine
2844 for (i = 1; i < mxml_get_line_number_end(mxml_find_node(c->pnseq, "RunSequence")); i++) {
2845 pt = mxml_get_node_at_line(c->pnseq, i);
2846 if (pt) {
2847 if (equal_ustring(mxml_get_name(pt), "Subroutine")) {
2848 if (equal_ustring(mxml_get_attribute(pt, "name"), mxml_get_attribute(pn, "name"))) {
2849 // put routine end line on end stack
2850 seq.subroutine_end_line[seq.stack_index] = mxml_get_line_number_end(pt);
2851 // go to first line of subroutine
2852 seq.current_line_number = mxml_get_line_number_start(pt) + 1;
2853 // put parameter(s) on stack
2854 if (mxml_get_value(pn)) {
2855 char p[256];
2856 if (strchr(mxml_get_value(pn), '$')) // evaluate message string if $ present
2857 mstrlcpy(p, eval_var(seq, c, mxml_get_value(pn)).c_str(), sizeof(p));
2858 else
2859 mstrlcpy(p, mxml_get_value(pn), sizeof(p)); // treat message as string
2860
2861 mstrlcpy(seq.subroutine_param[seq.stack_index], p, 256);
2862 }
2863 // increment stack
2864 seq.stack_index++;
2865 break;
2866 }
2867 }
2868 }
2869 }
2870 if (i == mxml_get_line_number_end(mxml_find_node(c->pnseq, "RunSequence"))) {
2871 seq_error(seq, c, msprintf("Subroutine '%s' not found", mxml_get_attribute(pn, "name")).c_str());
2872 }
2873 }
2874 }
2875
2876 /*---- <unknown> ----*/
2877 else {
2878 seq_error(seq, c, msprintf("Unknown statement \"%s\"", mxml_get_name(pn)).c_str());
2879 }
2880
2881 /* get steering parameters, since they might have been changed in between */
2882 SEQUENCER seq1;
2883 size = sizeof(seq1);
2884 db_get_record(c->hDB, hKeySeq, &seq1, &size, 0);
2885 seq.running = seq1.running;
2886 seq.finished = seq1.finished;
2887 seq.paused = seq1.paused;
2888 seq.debug = seq1.debug;
2889 seq.stop_after_run = seq1.stop_after_run;
2890 mstrlcpy(seq.message, seq1.message, sizeof(seq.message));
2891 memcpy(seq.next_filename, seq1.next_filename, sizeof(seq.next_filename));
2892
2893 // in debugging mode, pause after each step
2894 if (seq.debug && !skip_step)
2895 seq.paused = TRUE;
2896
2897 /* update current line number */
2898 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
2899}
2900
2901/*------------------------------------------------------------------*/
2902
2904 int status;
2905 HNDLE hKey;
2906
2907 c->odbs = gOdb->Chdir(c->odb_path.c_str(), true); // create /Sequencer
2908 assert(c->odbs);
2909
2910 status = db_find_key(c->hDB, 0, c->odb_path.c_str(), &c->hSeq);
2911 if (status != DB_SUCCESS) {
2912 cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot find /Sequencer, db_find_key() status %d", status);
2913 return;
2914 }
2915
2916 status = db_check_record(c->hDB, c->hSeq, "State", strcomb1(sequencer_str).c_str(), TRUE);
2917 if (status == DB_STRUCT_MISMATCH) {
2918 cm_msg(MERROR, "init_sequencer", "Sequencer error: mismatching /Sequencer/State structure, db_check_record() status %d", status);
2919 return;
2920 }
2921
2922 status = db_find_key(c->hDB, c->hSeq, "State", &hKey);
2923 if (status != DB_SUCCESS) {
2924 cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot find /Sequencer/State, db_find_key() status %d", status);
2925 return;
2926 }
2927
2928 int size = sizeof(seq);
2929 status = db_get_record1(c->hDB, hKey, &seq, &size, 0, strcomb1(sequencer_str).c_str());
2930 if (status != DB_SUCCESS) {
2931 cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot get /Sequencer/State, db_get_record1() status %d", status);
2932 return;
2933 }
2934
2935 if (strlen(seq.path) > 0 && seq.path[strlen(seq.path) - 1] != DIR_SEPARATOR) {
2936 mstrlcat(seq.path, DIR_SEPARATOR_STR, sizeof(seq.path));
2937 }
2938
2939 if (seq.filename[0])
2940 seq_open_file(seq.filename, seq, c);
2941
2943
2944 db_set_record(c->hDB, hKey, &seq, sizeof(seq), 0);
2945
2946 status = db_watch(c->hDB, hKey, seq_watch, c);
2947 if (status != DB_SUCCESS) {
2948 cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot watch /Sequencer/State, db_watch() status %d", status);
2949 return;
2950 }
2951
2952 bool b = false;
2953 c->odbs->RB("Command/Start script", &b, true);
2954 b = false;
2955 c->odbs->RB("Command/Stop immediately", &b, true);
2956 b = false;
2957 c->odbs->RB("Command/Stop after run", &b, true);
2958 b = false;
2959 c->odbs->RB("Command/Cancel stop after run", &b, true);
2960 b = false;
2961 c->odbs->RB("Command/Pause script", &b, true);
2962 b = false;
2963 c->odbs->RB("Command/Resume script", &b, true);
2964 b = false;
2965 c->odbs->RB("Command/Debug script", &b, true);
2966 b = false;
2967 c->odbs->RB("Command/Step over", &b, true);
2968 b = false;
2969 c->odbs->RB("Command/Load new file", &b, true);
2970
2971 status = db_find_key(c->hDB, c->hSeq, "Command", &hKey);
2972 if (status != DB_SUCCESS) {
2973 cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot find /Sequencer/Command, db_find_key() status %d", status);
2974 return;
2975 }
2976
2978 if (status != DB_SUCCESS) {
2979 cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot watch /Sequencer/Command, db_watch() status %d", status);
2980 return;
2981 }
2982}
2983
2984/*------------------------------------------------------------------*/
2985
2986int main(int argc, const char *argv[]) {
2987 int daemon = FALSE;
2988 int status, ch;
2989 char midas_hostname[256];
2990 char midas_expt[256];
2991 std::string seq_name = "";
2992
2993 setbuf(stdout, NULL);
2994 setbuf(stderr, NULL);
2995#ifdef SIGPIPE
2996 /* avoid getting killed by "Broken pipe" signals */
2997 signal(SIGPIPE, SIG_IGN);
2998#endif
2999
3000 /* get default from environment */
3001 cm_get_environment(midas_hostname, sizeof(midas_hostname), midas_expt, sizeof(midas_expt));
3002
3003 /* parse command line parameters */
3004 for (int i = 1; i < argc; i++) {
3005 if (argv[i][0] == '-' && argv[i][1] == 'D') {
3006 daemon = TRUE;
3007 } else if (argv[i][0] == '-') {
3008 if (i + 1 >= argc || argv[i + 1][0] == '-')
3009 goto usage;
3010 if (argv[i][1] == 'h')
3011 mstrlcpy(midas_hostname, argv[++i], sizeof(midas_hostname));
3012 else if (argv[i][1] == 'e')
3013 mstrlcpy(midas_expt, argv[++i], sizeof(midas_hostname));
3014 else if (argv[i][1] == 'c')
3015 seq_name = argv[++i];
3016 } else {
3017 usage:
3018 printf("usage: %s [-h Hostname[:port]] [-e Experiment] [-c Name] [-D]\n\n", argv[0]);
3019 printf(" -e experiment to connect to\n");
3020 printf(" -c Name of additional sequencer, i.e. \'Test\'\n");
3021 printf(" -h connect to midas server (mserver) on given host\n");
3022 printf(" -D become a daemon\n");
3023 return 0;
3024 }
3025 }
3026
3027 if (daemon) {
3028 printf("Becoming a daemon...\n");
3030 }
3031
3032 std::string prg_name = "Sequencer";
3033 std::string odb_path = "Sequencer";
3034
3035 if (!seq_name.empty()) {
3036 prg_name = std::string("Sequencer") + seq_name; // sequencer program name
3037 odb_path = std::string("Sequencer") + seq_name; // sequencer ODB path
3038 }
3039
3040#ifdef OS_LINUX
3041 std::string pid_filename;
3042 pid_filename += "/var/run/";
3043 pid_filename += prg_name;
3044 pid_filename += ".pid";
3045 /* write PID file */
3046 FILE *f = fopen(pid_filename.c_str(), "w");
3047 if (f != NULL) {
3048 fprintf(f, "%d", ss_getpid());
3049 fclose(f);
3050 }
3051#endif
3052
3053 /*---- connect to experiment ----*/
3054 status = cm_connect_experiment1(midas_hostname, midas_expt, prg_name.c_str(), NULL, DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
3056 return 1;
3057 else if (status == DB_INVALID_HANDLE) {
3058 std::string s = cm_get_error(status);
3059 puts(s.c_str());
3060 } else if (status != CM_SUCCESS) {
3061 std::string s = cm_get_error(status);
3062 puts(s.c_str());
3063 return 1;
3064 }
3065
3066 /* check if sequencer already running */
3067 status = cm_exist(prg_name.c_str(), TRUE);
3068 if (status == CM_SUCCESS) {
3069 printf("%s runs already.\n", prg_name.c_str());
3071 return 1;
3072 }
3073
3074 HNDLE hDB;
3076 gOdb = MakeMidasOdb(hDB);
3077
3078 SEQUENCER seq;
3079 SeqCon c;
3080
3081 c.seqp = &seq;
3082 c.hDB = hDB;
3083 c.seq_name = seq_name;
3084 c.prg_name = prg_name;
3085 c.odb_path = odb_path;
3086
3087 init_sequencer(seq, &c);
3088
3089 if (seq_name.empty()) {
3090 printf("Sequencer started. Stop with \"!\"\n");
3091 } else {
3092 printf("%s started.\n", prg_name.c_str());
3093 printf("Set ODB String \"/Alias/Sequencer%s\" to URL \"?cmd=sequencer&seq_name=%s\"\n", seq_name.c_str(), seq_name.c_str()); // FIXME: seq_name should be URL-encoded! K.O. Aug 2023
3094 printf("Stop with \"!\"\n");
3095 }
3096
3097 // if any commands are active, process them now
3098 seq_watch_command(hDB, 0, 0, &c);
3099
3100 /* initialize ss_getchar */
3101 ss_getchar(0);
3102
3103 /* main loop */
3104 do {
3105 try {
3106 sequencer(seq, &c);
3107 } catch (std::string &msg) {
3108 seq_error(seq, &c, msg.c_str());
3109 } catch (const char *msg) {
3110 seq_error(seq, &c, msg);
3111 }
3112
3113 status = cm_yield(0);
3114
3115 ch = 0;
3116 while (ss_kbhit()) {
3117 ch = ss_getchar(0);
3118 if (ch == -1)
3119 ch = getchar();
3120
3121 if ((char) ch == '!')
3122 break;
3123 }
3124
3125 } while (status != RPC_SHUTDOWN && ch != '!');
3126
3127 /* reset terminal */
3129
3130 /* close network connection to server */
3132
3133 return 0;
3134}
3135
3136/*------------------------------------------------------------------*/
3137
/* end of alfunctioncode */
3140
3141/* emacs
3142 * Local Variables:
3143 * tab-width: 8
3144 * c-basic-offset: 3
3145 * indent-tabs-mode: nil
3146 * End:
3147 */
#define FALSE
Definition cfortran.h:309
HNDLE hDB
PMXML_NODE pnseq
SEQUENCER * seqp
std::string prg_name
MVOdb * odbs
std::string odb_path
HNDLE hSeq
std::string seq_name
static bool exists(const std::string &name)
Definition odbxx.cxx:76
void set_auto_refresh_read(bool f)
Definition odbxx.h:1368
void set_auto_create(bool f)
Definition odbxx.h:1383
std::string s()
Definition odbxx.h:1465
static void usage()
INT cm_yield(INT millisec)
Definition midas.cxx:5660
INT cm_get_experiment_database(HNDLE *hDB, HNDLE *hKeyClient)
Definition midas.cxx:3027
INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5304
INT cm_connect_experiment1(const char *host_name, const char *default_exp_name, const char *client_name, void(*func)(char *), INT odb_size, DWORD watchdog_timeout)
Definition midas.cxx:2313
std::string cm_expand_env(const char *str)
Definition midas.cxx:7721
INT cm_disconnect_experiment(void)
Definition midas.cxx:2862
std::string cm_get_path()
Definition midas.cxx:1553
INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size)
Definition midas.cxx:2150
INT cm_exist(const char *name, BOOL bUnique)
Definition midas.cxx:7531
#define CM_SUCCESS
Definition midas.h:582
#define CM_DEFERRED_TRANSITION
Definition midas.h:591
#define CM_WRONG_PASSWORD
Definition midas.h:589
#define DB_STRUCT_MISMATCH
Definition midas.h:655
#define DB_INVALID_HANDLE
Definition midas.h:636
#define DB_INVALID_PARAM
Definition midas.h:640
#define DB_FILE_ERROR
Definition midas.h:648
#define DB_SUCCESS
Definition midas.h:632
#define DB_NO_KEY
Definition midas.h:643
#define DB_CREATED
Definition midas.h:633
#define RPC_SHUTDOWN
Definition midas.h:708
unsigned int DWORD
Definition mcstd.h:51
#define TID_DOUBLE
Definition midas.h:343
#define TID_BOOL
Definition midas.h:340
#define TR_START
Definition midas.h:405
#define TR_SYNC
Definition midas.h:358
#define TR_MTHREAD
Definition midas.h:361
#define STATE_STOPPED
Definition midas.h:305
#define MINFO
Definition midas.h:560
#define MLOG
Definition midas.h:563
#define TID_INT32
Definition midas.h:339
#define TID_LINK
Definition midas.h:350
#define TID_STRING
Definition midas.h:346
#define MERROR
Definition midas.h:559
#define STATE_RUNNING
Definition midas.h:307
#define MTALK
Definition midas.h:564
#define MDEBUG
Definition midas.h:561
#define TID_INT
Definition midas.h:338
#define TR_STOP
Definition midas.h:406
#define TID_LAST
Definition midas.h:354
#define O_TEXT
Definition msystem.h:227
BOOL ss_kbhit()
Definition system.cxx:3736
DWORD ss_millitime()
Definition system.cxx:3465
INT ss_getchar(BOOL reset)
Definition system.cxx:7581
INT ss_getpid(void)
Definition system.cxx:1379
std::string ss_execs(const char *cmd)
Definition system.cxx:2309
std::string ss_replace_env_variables(const std::string &inputPath)
Definition system.cxx:2284
INT ss_daemon_init(BOOL keep_stdout)
Definition system.cxx:2073
INT ss_sleep(INT millisec)
Definition system.cxx:3700
std::string cm_get_error(INT code)
Definition midas.cxx:469
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:931
INT cm_msg_retrieve(INT n_message, char *message, INT buf_size)
Definition midas.cxx:1350
BOOL equal_ustring(const char *str1, const char *str2)
Definition odb.cxx:3285
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
Definition odb.cxx:6917
INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
Definition odb.cxx:3933
INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT *buf_size, DWORD type, BOOL create)
Definition odb.cxx:5185
INT db_save_json(HNDLE hDB, HNDLE hKey, const char *filename, int flags)
Definition odb.cxx:10548
std::string strcomb1(const char **list)
Definition odb.cxx:668
INT db_save_xml(HNDLE hDB, HNDLE hKey, const char *filename)
Definition odb.cxx:9503
INT db_get_record1(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT align, const char *rec_str)
Definition odb.cxx:11834
INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
Definition odb.cxx:3392
INT db_check_record(HNDLE hDB, HNDLE hKey, const char *keyname, const char *rec_str, BOOL correct)
Definition odb.cxx:13003
INT db_get_record(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT align)
Definition odb.cxx:11736
INT db_save(HNDLE hDB, HNDLE hKey, const char *filename, BOOL bRemote)
Definition odb.cxx:9263
INT db_get_key(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6043
INT db_load(HNDLE hDB, HNDLE hKeyRoot, const char *filename, BOOL bRemote)
Definition odb.cxx:8139
INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7668
INT db_watch(HNDLE hDB, HNDLE hKey, void(*dispatcher)(INT, INT, INT, void *), void *info)
Definition odb.cxx:13845
INT db_delete(HNDLE hDB, HNDLE hKeyRoot, const char *odb_path)
Definition odb.cxx:3999
INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10865
void strarrayindex(char *odbpath, int *index1, int *index2)
Definition odb.cxx:3357
INT db_set_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type)
Definition odb.cxx:5028
INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4256
INT db_find_keys(HNDLE hDB, HNDLE hKeyRoot, const char *odbpath, std::vector< HNDLE > &hKeyVector)
Definition odb.cxx:4352
INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
Definition odb.cxx:12320
INT db_set_data_index1(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type, BOOL bNotify)
Definition odb.cxx:7844
INT db_sscanf(const char *data_str, void *data, INT *data_size, INT i, DWORD tid)
Definition odb.cxx:11336
INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
Definition odb.cxx:7523
const char * rpc_tid_name(INT id)
Definition midas.cxx:11895
static void seq_watch(HNDLE hDB, HNDLE hKeyChanged, int index, void *info)
void seq_array_index(SEQUENCER &seq, SeqCon *c, char *odbpath, int *index1, int *index2)
int concatenate(SEQUENCER &seq, SeqCon *c, char *result, int size, char *value)
static void seq_stop_after_run(SEQUENCER &seq, SeqCon *c)
MVOdb * gOdb
static void seq_start_next(SEQUENCER &seq, SeqCon *c)
static void seq_stop(SEQUENCER &seq, SeqCon *c)
void seq_write(const SEQUENCER &seq, SeqCon *c)
int strbreak(char *str, char list[][XNAME_LENGTH], int size, const char *brk, BOOL ignore_quotes)
void strsubst(char *string, int size, const char *pattern, const char *subst)
static std::string qtoString(std::string filename, int line, int level)
static void seq_watch_command(HNDLE hDB, HNDLE hKeyChanged, int index, void *info)
char * stristr(const char *str, const char *pattern)
static BOOL goto_at_exit(SEQUENCER &seq, SeqCon *c)
void seq_read(SEQUENCER *seq, SeqCon *c)
static BOOL msl_parse(SEQUENCER &seq, SeqCon *c, int level, const char *cpath, const char *filename, const char *xml_filename, char *error, int error_size, int *error_line)
void init_sequencer(SEQUENCER &seq, SeqCon *c)
static void seq_step_over(SEQUENCER &seq, SeqCon *c)
static void seq_cancel_stop_after_run(SEQUENCER &seq, SeqCon *c)
static void seq_pause(SEQUENCER &seq, SeqCon *c, bool flag)
void sequencer(SEQUENCER &seq, SeqCon *c)
static std::string toString(int v)
void seq_error(SEQUENCER &seq, SeqCon *c, const char *str)
int set_all_matching(HNDLE hDB, HNDLE hBaseKey, const char *odbpath, char *value, int index1, int index2, int notify)
static std::string q(const char *s)
std::string eval_var(SEQUENCER &seq, SeqCon *c, std::string value)
bool loadMSL(MVOdb *o, const std::string &filename)
int eval_condition(SEQUENCER &seq, SeqCon *c, const char *condition)
static void seq_start(SEQUENCER &seq, SeqCon *c, bool debug)
bool is_valid_number(const char *str)
void seq_clear(SEQUENCER &seq)
static void seq_open_file(const char *str, SEQUENCER &seq, SeqCon *c)
int main()
Definition hwtest.cxx:23
void ** info
Definition fesimdaq.cxx:41
HNDLE hKey
INT run_number[2]
Definition mana.cxx:246
DWORD n[4]
Definition mana.cxx:247
INT index
Definition mana.cxx:271
char param[10][256]
Definition mana.cxx:250
void * data
Definition mana.cxx:268
BOOL debug
debug printouts
Definition mana.cxx:254
BOOL daemon
Definition mana.cxx:258
INT type
Definition mana.cxx:269
HNDLE hDB
main ODB handle
Definition mana.cxx:207
KEY key
Definition mdump.cxx:34
INT i
Definition mdump.cxx:32
std::vector< FMT_ID > eq
Definition mdump.cxx:55
#define DWORD
Definition mhdump.cxx:31
std::string msprintf(const char *format,...)
Definition midas.cxx:419
#define JSFLAG_RECURSE
Definition midas.h:1712
#define DIR_SEPARATOR
Definition midas.h:193
INT HNDLE
Definition midas.h:132
DWORD BOOL
Definition midas.h:105
#define DIR_SEPARATOR_STR
Definition midas.h:194
#define DEFAULT_WATCHDOG_TIMEOUT
Definition midas.h:290
#define JSFLAG_FOLLOW_LINKS
Definition midas.h:1711
#define JSFLAG_OMIT_LAST_WRITTEN
Definition midas.h:1715
#define TRUE
Definition midas.h:182
#define DEFAULT_ODB_SIZE
Definition midas.h:270
#define read(n, a, f)
#define name(x)
Definition midas_macro.h:24
#define XNAME_LENGTH
INT j
Definition odbhist.cxx:40
double value[100]
Definition odbhist.cxx:42
char str[256]
Definition odbhist.cxx:33
DWORD status
Definition odbhist.cxx:39
#define SEQ_NEST_LEVEL_IF
Definition sequencer.h:4
#define SEQUENCER_STR(_name)
Definition sequencer.h:56
#define SEQ_NEST_LEVEL_LOOP
Definition sequencer.h:3
#define SEQ_NEST_LEVEL_SUB
Definition sequencer.h:5
Definition midas.h:1027
INT num_values
Definition midas.h:1029
DWORD type
Definition midas.h:1028
INT item_size
Definition midas.h:1033
int serror_line
Definition sequencer.h:15
char wait_odb[256]
Definition sequencer.h:52
int if_index
Definition sequencer.h:36
int if_line[SEQ_NEST_LEVEL_IF]
Definition sequencer.h:37
float wait_value
Definition sequencer.h:46
int stack_index
Definition sequencer.h:40
int loop_end_line[SEQ_NEST_LEVEL_LOOP]
Definition sequencer.h:29
BOOL transition_request
Definition sequencer.h:26
BOOL stop_after_run
Definition sequencer.h:25
float wait_limit
Definition sequencer.h:47
float timeout_value
Definition sequencer.h:48
int sloop_start_line[SEQ_NEST_LEVEL_LOOP]
Definition sequencer.h:28
int error_line
Definition sequencer.h:14
BOOL finished
Definition sequencer.h:19
int subroutine_return_line[SEQ_NEST_LEVEL_SUB]
Definition sequencer.h:42
BOOL message_wait
Definition sequencer.h:17
int loop_start_line[SEQ_NEST_LEVEL_LOOP]
Definition sequencer.h:27
int subroutine_end_line[SEQ_NEST_LEVEL_SUB]
Definition sequencer.h:41
int if_else_line[SEQ_NEST_LEVEL_IF]
Definition sequencer.h:38
int sloop_end_line[SEQ_NEST_LEVEL_LOOP]
Definition sequencer.h:30
char subdir[256]
Definition sequencer.h:33
BOOL debug
Definition sequencer.h:21
char message[256]
Definition sequencer.h:16
BOOL paused
Definition sequencer.h:20
int if_endif_line[SEQ_NEST_LEVEL_IF]
Definition sequencer.h:39
int follow_libraries
Definition sequencer.h:24
BOOL new_file
Definition sequencer.h:8
DWORD start_time
Definition sequencer.h:50
int subroutine_call_line[SEQ_NEST_LEVEL_SUB]
Definition sequencer.h:43
int loop_n[SEQ_NEST_LEVEL_LOOP]
Definition sequencer.h:32
int ssubroutine_call_line[SEQ_NEST_LEVEL_SUB]
Definition sequencer.h:44
char subroutine_param[SEQ_NEST_LEVEL_SUB][256]
Definition sequencer.h:45
int subdir_end_line
Definition sequencer.h:34
BOOL running
Definition sequencer.h:18
char filename[256]
Definition sequencer.h:10
int loop_counter[SEQ_NEST_LEVEL_LOOP]
Definition sequencer.h:31
int current_line_number
Definition sequencer.h:22
char error[256]
Definition sequencer.h:13
float timeout_limit
Definition sequencer.h:49
int scurrent_line_number
Definition sequencer.h:23
char last_msg[10]
Definition sequencer.h:53
char wait_type[32]
Definition sequencer.h:51
char sfilename[256]
Definition sequencer.h:11
char path[256]
Definition sequencer.h:9
int subdir_not_notify
Definition sequencer.h:35
char next_filename[10][256]
Definition sequencer.h:12
double d
Definition system.cxx:1313
char c
Definition system.cxx:1312
double te_interp(const char *expression, int *error)
Definition tinyexpr.c:690
static double e(void)
Definition tinyexpr.c:136
static void pn(const te_expr *n, int depth)
Definition tinyexpr.c:702
static te_expr * list(state *s)
Definition tinyexpr.c:567