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