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