MIDAS
Loading...
Searching...
No Matches
Sequencer Functions

Classes

class  SeqCon
 

Functions

 SEQUENCER_STR (sequencer_str)
 
char * stristr (const char *str, const char *pattern)
 
void strsubst (char *string, int size, const char *pattern, const char *subst)
 
static std::string toString (int v)
 
static std::string qtoString (std::string filename, int line, int level)
 
static std::string q (const char *s)
 
bool is_valid_number (const char *str)
 
void seq_error (SEQUENCER &seq, SeqCon *c, const char *str)
 
int strbreak (char *str, char list[][XNAME_LENGTH], int size, const char *brk, BOOL ignore_quotes)
 
std::string eval_var (SEQUENCER &seq, SeqCon *c, std::string value)
 
int concatenate (SEQUENCER &seq, SeqCon *c, char *result, int size, char *value)
 
int eval_condition (SEQUENCER &seq, SeqCon *c, const char *condition)
 
bool loadMSL (MVOdb *o, const std::string &filename)
 
static BOOL msl_parse (SEQUENCER &seq, SeqCon *c, int level, const char *cpath, const char *filename, const char *xml_filename, char *error, int error_size, int *error_line)
 
void seq_read (SEQUENCER *seq, SeqCon *c)
 
void seq_write (const SEQUENCER &seq, SeqCon *c)
 
void seq_clear (SEQUENCER &seq)
 
static void seq_start (SEQUENCER &seq, SeqCon *c, bool debug)
 
static void seq_stop (SEQUENCER &seq, SeqCon *c)
 
static void seq_stop_after_run (SEQUENCER &seq, SeqCon *c)
 
static void seq_cancel_stop_after_run (SEQUENCER &seq, SeqCon *c)
 
static void seq_pause (SEQUENCER &seq, SeqCon *c, bool flag)
 
static BOOL goto_at_exit (SEQUENCER &seq, SeqCon *c)
 
static void seq_step_over (SEQUENCER &seq, SeqCon *c)
 
static void seq_open_file (const char *str, SEQUENCER &seq, SeqCon *c)
 
static void seq_start_next (SEQUENCER &seq, SeqCon *c)
 
static void seq_watch (HNDLE hDB, HNDLE hKeyChanged, int index, void *info)
 
static void seq_clear_wait_state (SEQUENCER &seq)
 
static std::string seq_goto_context (SeqCon *c, int line)
 
static BOOL seq_goto_line (SEQUENCER &seq, SeqCon *c, int target_sline)
 
static void seq_watch_command (HNDLE hDB, HNDLE hKeyChanged, int index, void *info)
 
void seq_array_index (SEQUENCER &seq, SeqCon *c, char *odbpath, int *index1, int *index2)
 
int set_all_matching (HNDLE hDB, HNDLE hBaseKey, const char *odbpath, char *value, int index1, int index2, int notify)
 
void sequencer (SEQUENCER &seq, SeqCon *c)
 
void init_sequencer (SEQUENCER &seq, SeqCon *c)
 
int main (int argc, const char *argv[])
 

Variables

MVOdb * gOdb = NULL
 

Detailed Description

dox


Function Documentation

◆ concatenate()

int concatenate ( SEQUENCER seq,
SeqCon c,
char *  result,
int  size,
char *  value 
)

Definition at line 378 of file msequencer.cxx.

378 {
379 char list[100][XNAME_LENGTH];
380 int i, n;
381
382 n = strbreak(value, list, 100, ",", FALSE);
383
384 result[0] = 0;
385 for (i = 0; i < n; i++) {
386 std::string str = eval_var(seq, c, std::string(list[i]));
387 mstrlcat(result, str.c_str(), size);
388 }
389
390 return TRUE;
391}
#define FALSE
Definition cfortran.h:309
int strbreak(char *str, char list[][XNAME_LENGTH], int size, const char *brk, BOOL ignore_quotes)
std::string eval_var(SEQUENCER &seq, SeqCon *c, std::string value)
DWORD n[4]
Definition mana.cxx:247
INT i
Definition mdump.cxx:32
#define TRUE
Definition midas.h:182
#define XNAME_LENGTH
double value[100]
Definition odbhist.cxx:42
char str[256]
Definition odbhist.cxx:33
char c
Definition system.cxx:1312
static te_expr * list(state *s)
Definition tinyexpr.c:567
Here is the call graph for this function:
Here is the caller graph for this function:

◆ eval_condition()

int eval_condition ( SEQUENCER seq,
SeqCon c,
const char *  condition 
)

Definition at line 395 of file msequencer.cxx.

395 {
396 int i;
397 double value1, value2;
398 char value1_str[256], value2_str[256], str[256], op[3], *p;
399 std::string value1_var, value2_var;
400
401 // strip leading and trailing space
402 p = (char *)condition;
403 while (*p == ' ')
404 p++;
405 strcpy(str, p);
406
407 // strip any comment '#'
408 if (strchr(str, '#'))
409 *strchr(str, '#') = 0;
410
411 while (strlen(str) > 0 && (str[strlen(str)-1] == ' '))
412 str[strlen(str)-1] = 0;
413
414 // strip enclosing '()'
415 if (str[0] == '(' && strlen(str) > 0 && str[strlen(str)-1] == ')') {
416 mstrlcpy(value1_str, str+1, sizeof(value1_str));
417 mstrlcpy(str, value1_str, sizeof(str));
418 str[strlen(str)-1] = 0;
419 }
420
421 op[1] = op[2] = 0;
422
423 /* find value and operator */
424 for (i = 0; i < (int) strlen(str); i++)
425 if (strchr("<>=!&", str[i]) != NULL)
426 break;
427 mstrlcpy(value1_str, str, i + 1);
428 while (value1_str[strlen(value1_str) - 1] == ' ')
429 value1_str[strlen(value1_str) - 1] = 0;
430 op[0] = str[i];
431 if (strchr("<>=!&", str[i + 1]) != NULL)
432 op[1] = str[++i];
433
434 for (i++; str[i] == ' '; i++);
435 mstrlcpy(value2_str, str + i, sizeof(value2_str));
436
437 value1_var = eval_var(seq, c, value1_str);
438 value2_var = eval_var(seq, c, value2_str);
439
440 if (!is_valid_number(value1_var.c_str()) || !is_valid_number(value2_var.c_str())) {
441 // string comparison
442 if (strcmp(op, "=") == 0)
443 return equal_ustring(value1_var.c_str(), value2_var.c_str()) ? 1 : 0;
444 if (strcmp(op, "==") == 0)
445 return equal_ustring(value1_var.c_str(), value2_var.c_str()) ? 1 : 0;
446 if (strcmp(op, "!=") == 0)
447 return equal_ustring(value1_var.c_str(), value2_var.c_str()) ? 0 : 1;
448 // invalid operator for string comparisons
449 return -1;
450 }
451
452 // numeric comparison
453 value1 = atof(value1_var.c_str());
454 value2 = atof(value2_var.c_str());
455
456 /* now do logical operation */
457 if (strcmp(op, "=") == 0)
458 if (value1 == value2)
459 return 1;
460 if (strcmp(op, "==") == 0)
461 if (value1 == value2)
462 return 1;
463 if (strcmp(op, "!=") == 0)
464 if (value1 != value2)
465 return 1;
466 if (strcmp(op, "<") == 0)
467 if (value1 < value2)
468 return 1;
469 if (strcmp(op, ">") == 0)
470 if (value1 > value2)
471 return 1;
472 if (strcmp(op, "<=") == 0)
473 if (value1 <= value2)
474 return 1;
475 if (strcmp(op, ">=") == 0)
476 if (value1 >= value2)
477 return 1;
478 if (strcmp(op, "&") == 0)
479 if (((unsigned int) value1 & (unsigned int) value2) > 0)
480 return 1;
481
482 return 0;
483}
BOOL equal_ustring(const char *str1, const char *str2)
Definition odb.cxx:3285
bool is_valid_number(const char *str)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ eval_var()

std::string eval_var ( SEQUENCER seq,
SeqCon c,
std::string  value 
)

Definition at line 267 of file msequencer.cxx.

267 {
268 std::string result;
269 std::string xpath;
270 xpath += "/";
271 xpath += c->odb_path;
272
273 //printf("eval [%s] xpath [%s]\n", value.c_str(), xpath.c_str());
274
275 result = value;
276
277 // replace all $... with value
278 int i1, i2;
279 std::string vsubst;
280 while ((i1 = (int)result.find("$")) != (int)std::string::npos) {
281 std::string s = result.substr(i1 + 1);
282 if (std::isdigit(s[0])) {
283 // find end of number
284 for (i2 = i1 + 1; std::isdigit(result[i2]);)
285 i2++;
286
287 // replace all $<number> with subroutine parameters
288 int index = atoi(s.c_str());
289 if (seq.stack_index > 0) {
290 std::istringstream f(seq.subroutine_param[seq.stack_index - 1]);
291 std::vector<std::string> param;
292 std::string sp;
293 while (std::getline(f, sp, ','))
294 param.push_back(sp);
295 if (index == 0 || index > (int)param.size())
296 throw "Parameter \"$" + std::to_string(index) + "\" not valid";
297 vsubst = param[index - 1];
298 if (vsubst[0] == '$')
299 vsubst = eval_var(seq, c, vsubst);
300 } else
301 throw "Parameter \"$" + std::to_string(index) + "\" not valid";
302 } else {
303 // find end of string
304 for (i2 = i1 + 1; std::isalnum(result[i2]) || result[i2] == '_';)
305 i2++;
306 s = s.substr(0, i2 - i1 - 1);
307 if (result[i2] == '[') {
308 // array
309 auto sindex = result.substr(i2+1);
310 int i = sindex.find(']');
311 if (i == (int)std::string::npos)
312 throw "Variable \"" + result +"\" does not contain ']'";
313 sindex = sindex.substr(0, i);
314 sindex = eval_var(seq, c, sindex);
315 int index;
316 try {
317 index = std::stoi(sindex);
318 } catch (...) {
319 throw "Variable \"" + s + "\" has invalid index";
320 }
321
322 try {
323 midas::odb o(xpath + "/Variables/" + s);
324 std::vector<std::string> sv = o;
325 vsubst = sv[index];
326 } catch (...) {
327 throw "Variable \"" + s + "\" not found";
328 }
329 while (result[i2] && result[i2] != ']')
330 i2++;
331 if (!result[i2])
332 throw "Variable \"" + result +"\" does not contain ']'";
333 if (result[i2] == ']')
334 i2++;
335 } else {
336 try {
337 if (!midas::odb::exists(xpath + "/Variables/" + s))
338 throw "Variable \"" + s + "\" not found";
339 midas::odb o(xpath + "/Variables/" + s);
340 vsubst = o;
341 } catch (...) {
342 throw "Variable \"" + s + "\" not found";
343 }
344 }
345 }
346
347 result = result.substr(0, i1) + vsubst + result.substr(i2);
348 }
349
350 //printf("eval [%s] xpath [%s] result [%s]\n", value.c_str(), xpath.c_str(), result.c_str());
351
352 // check if result is a list
353 if (result.find(",") != std::string::npos)
354 return result;
355
356 // check for expression
357 int error;
358 double r = te_interp(result.c_str(), &error);
359 if (error > 0) {
360 // check if result is only a string
361 if (!std::isdigit(result[0]) && result[0] != '-')
362 return result;
363
364 throw "Error in expression \"" + result + "\" position " + std::to_string(error - 1);
365 }
366
367 if (r == (int) r)
368 return std::to_string((int) r);
369
370 std::ostringstream os;
371 os << std::scientific << r;
372
373 return os.str();
374}
static bool exists(const std::string &name)
Definition odbxx.cxx:76
INT index
Definition mana.cxx:271
char param[10][256]
Definition mana.cxx:250
int stack_index
Definition sequencer.h:40
char subroutine_param[SEQ_NEST_LEVEL_SUB][256]
Definition sequencer.h:45
double te_interp(const char *expression, int *error)
Definition tinyexpr.c:690
Here is the call graph for this function:
Here is the caller graph for this function:

◆ goto_at_exit()

static BOOL goto_at_exit ( SEQUENCER seq,
SeqCon c 
)
static

Definition at line 1241 of file msequencer.cxx.

1241 {
1242 seq_read(&seq, c);
1243
1244 // search ATEXIT subroutine
1245 for (int i = 1; i < mxml_get_line_number_end(mxml_find_node(c->pnseq, "RunSequence")); i++) {
1246 PMXML_NODE pt = mxml_get_node_at_line(c->pnseq, i);
1247 if (pt) {
1248 if (equal_ustring(mxml_get_name(pt), "Subroutine")) {
1249 if (equal_ustring(mxml_get_attribute(pt, "name"), "ATEXIT")) {
1250
1251 if (seq.stack_index == SEQ_NEST_LEVEL_SUB) {
1252 seq_error(seq, c, "Maximum subroutine level exceeded");
1253 return FALSE;
1254 }
1255
1256 // put routine end line on end stack
1257 seq.subroutine_end_line[seq.stack_index] = mxml_get_line_number_end(pt);
1258 seq.ssubroutine_call_line[seq.stack_index] = atoi(mxml_get_attribute(pt, "l"));
1259 seq.subroutine_return_line[seq.stack_index] = -1; // indicates exit on return
1260
1261 // put routine end line on end stack
1262 seq.subroutine_end_line[seq.stack_index] = mxml_get_line_number_end(pt);
1263
1264 // go to first line of subroutine
1265 seq.current_line_number = mxml_get_line_number_start(pt) + 1;
1266
1267 // increment stack
1268 seq.stack_index++;
1269
1270 seq_write(seq, c);
1271
1272 return TRUE;
1273 }
1274 }
1275 }
1276 }
1277
1278 // look for atexit subroutine
1279 return FALSE;
1280}
void seq_write(const SEQUENCER &seq, SeqCon *c)
void seq_read(SEQUENCER *seq, SeqCon *c)
void seq_error(SEQUENCER &seq, SeqCon *c, const char *str)
#define SEQ_NEST_LEVEL_SUB
Definition sequencer.h:5
int subroutine_return_line[SEQ_NEST_LEVEL_SUB]
Definition sequencer.h:42
int subroutine_end_line[SEQ_NEST_LEVEL_SUB]
Definition sequencer.h:41
int ssubroutine_call_line[SEQ_NEST_LEVEL_SUB]
Definition sequencer.h:44
int current_line_number
Definition sequencer.h:22
Here is the call graph for this function:
Here is the caller graph for this function:

◆ init_sequencer()

void init_sequencer ( SEQUENCER seq,
SeqCon c 
)

Definition at line 3057 of file msequencer.cxx.

3057 {
3058 int status;
3059 HNDLE hKey;
3060
3061 c->odbs = gOdb->Chdir(c->odb_path.c_str(), true); // create /Sequencer
3062 assert(c->odbs);
3063
3064 status = db_find_key(c->hDB, 0, c->odb_path.c_str(), &c->hSeq);
3065 if (status != DB_SUCCESS) {
3066 cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot find /Sequencer, db_find_key() status %d", status);
3067 return;
3068 }
3069
3070 status = db_check_record(c->hDB, c->hSeq, "State", strcomb1(sequencer_str).c_str(), TRUE);
3071 if (status == DB_STRUCT_MISMATCH) {
3072 cm_msg(MERROR, "init_sequencer", "Sequencer error: mismatching /Sequencer/State structure, db_check_record() status %d", status);
3073 return;
3074 }
3075
3076 status = db_find_key(c->hDB, c->hSeq, "State", &hKey);
3077 if (status != DB_SUCCESS) {
3078 cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot find /Sequencer/State, db_find_key() status %d", status);
3079 return;
3080 }
3081
3082 int size = sizeof(seq);
3083 status = db_get_record1(c->hDB, hKey, &seq, &size, 0, strcomb1(sequencer_str).c_str());
3084 if (status != DB_SUCCESS) {
3085 cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot get /Sequencer/State, db_get_record1() status %d", status);
3086 return;
3087 }
3088
3089 if (strlen(seq.path) > 0 && seq.path[strlen(seq.path) - 1] != DIR_SEPARATOR) {
3090 mstrlcat(seq.path, DIR_SEPARATOR_STR, sizeof(seq.path));
3091 }
3092
3093 if (seq.filename[0])
3094 seq_open_file(seq.filename, seq, c);
3095
3097
3098 db_set_record(c->hDB, hKey, &seq, sizeof(seq), 0);
3099
3100 status = db_watch(c->hDB, hKey, seq_watch, c);
3101 if (status != DB_SUCCESS) {
3102 cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot watch /Sequencer/State, db_watch() status %d", status);
3103 return;
3104 }
3105
3106 midas::odb command = {
3107 {"Start script", false},
3108 {"Stop immediately", false},
3109 {"Stop after run", false},
3110 {"Cancel stop after run", false},
3111 {"Pause script", false},
3112 {"Resume script", false},
3113 {"Debug script", false},
3114 {"Step over", false},
3115 {"Load new file", false},
3116 {"Goto line", 0},
3117 };
3118 command.connect_and_fix_structure("/" + c->odb_path + "/Command");
3119
3120 status = db_find_key(c->hDB, c->hSeq, "Command", &hKey);
3121 if (status != DB_SUCCESS) {
3122 cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot find /Sequencer/Command, db_find_key() status %d", status);
3123 return;
3124 }
3125
3127 if (status != DB_SUCCESS) {
3128 cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot watch /Sequencer/Command, db_watch() status %d", status);
3129 return;
3130 }
3131}
void connect_and_fix_structure(std::string path)
Definition odbxx.cxx:1461
#define DB_STRUCT_MISMATCH
Definition midas.h:655
#define DB_SUCCESS
Definition midas.h:632
#define MERROR
Definition midas.h:559
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:931
std::string strcomb1(const char **list)
Definition odb.cxx:668
INT db_get_record1(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT align, const char *rec_str)
Definition odb.cxx:11834
INT db_check_record(HNDLE hDB, HNDLE hKey, const char *keyname, const char *rec_str, BOOL correct)
Definition odb.cxx:13003
INT db_watch(HNDLE hDB, HNDLE hKey, void(*dispatcher)(INT, INT, INT, void *), void *info)
Definition odb.cxx:13845
INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4256
INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
Definition odb.cxx:12320
static void seq_watch(HNDLE hDB, HNDLE hKeyChanged, int index, void *info)
MVOdb * gOdb
static void seq_watch_command(HNDLE hDB, HNDLE hKeyChanged, int index, void *info)
static void seq_open_file(const char *str, SEQUENCER &seq, SeqCon *c)
HNDLE hKey
#define DIR_SEPARATOR
Definition midas.h:193
INT HNDLE
Definition midas.h:132
#define DIR_SEPARATOR_STR
Definition midas.h:194
DWORD status
Definition odbhist.cxx:39
BOOL transition_request
Definition sequencer.h:26
char filename[256]
Definition sequencer.h:10
char path[256]
Definition sequencer.h:9
Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_valid_number()

bool is_valid_number ( const char *  str)

Definition at line 147 of file msequencer.cxx.

147 {
148 std::string s(str);
149 std::stringstream ss;
150 ss << s;
151 double num = 0;
152 ss >> num;
153 if (ss.good())
154 return false;
155 else if (num == 0 && s[0] != '0')
156 return false;
157 else if (s[0] == 0)
158 return false;
159 return true;
160}
Here is the caller graph for this function:

◆ loadMSL()

bool loadMSL ( MVOdb *  o,
const std::string &  filename 
)

Definition at line 487 of file msequencer.cxx.

487 {
488
489 std::ifstream file(filename);
490 if (!file.is_open())
491 return false;
492
493 std::vector<std::string> lines;
494 std::string line;
495
496 // read all lines from file and put it into array
497 while (std::getline(file, line))
498 lines.push_back(line);
499 file.close();
500
501 o->WSA("Script/Lines", lines, 0);
502
503 return true;
504}
Here is the caller graph for this function:

◆ main()

int main ( int  argc,
const char *  argv[] 
)

Definition at line 3135 of file msequencer.cxx.

3135 {
3136 int daemon = FALSE;
3137 int status, ch;
3138 char midas_hostname[256];
3139 char midas_expt[256];
3140 std::string seq_name = "";
3141
3142 setbuf(stdout, NULL);
3143 setbuf(stderr, NULL);
3144#ifdef SIGPIPE
3145 /* avoid getting killed by "Broken pipe" signals */
3146 signal(SIGPIPE, SIG_IGN);
3147#endif
3148
3149 /* get default from environment */
3150 cm_get_environment(midas_hostname, sizeof(midas_hostname), midas_expt, sizeof(midas_expt));
3151
3152 /* parse command line parameters */
3153 for (int i = 1; i < argc; i++) {
3154 if (argv[i][0] == '-' && argv[i][1] == 'D') {
3155 daemon = TRUE;
3156 } else if (argv[i][0] == '-') {
3157 if (i + 1 >= argc || argv[i + 1][0] == '-')
3158 goto usage;
3159 if (argv[i][1] == 'h')
3160 mstrlcpy(midas_hostname, argv[++i], sizeof(midas_hostname));
3161 else if (argv[i][1] == 'e')
3162 mstrlcpy(midas_expt, argv[++i], sizeof(midas_hostname));
3163 else if (argv[i][1] == 'c')
3164 seq_name = argv[++i];
3165 } else {
3166 usage:
3167 printf("usage: %s [-h Hostname[:port]] [-e Experiment] [-c Name] [-D]\n\n", argv[0]);
3168 printf(" -e experiment to connect to\n");
3169 printf(" -c Name of additional sequencer, i.e. \'Test\'\n");
3170 printf(" -h connect to midas server (mserver) on given host\n");
3171 printf(" -D become a daemon\n");
3172 return 0;
3173 }
3174 }
3175
3176 if (daemon) {
3177 printf("Becoming a daemon...\n");
3179 }
3180
3181 std::string prg_name = "Sequencer";
3182 std::string odb_path = "Sequencer";
3183
3184 if (!seq_name.empty()) {
3185 prg_name = std::string("Sequencer") + seq_name; // sequencer program name
3186 odb_path = std::string("Sequencer") + seq_name; // sequencer ODB path
3187 }
3188
3189#ifdef OS_LINUX
3190 std::string pid_filename;
3191 pid_filename += "/var/run/";
3192 pid_filename += prg_name;
3193 pid_filename += ".pid";
3194 /* write PID file */
3195 FILE *f = fopen(pid_filename.c_str(), "w");
3196 if (f != NULL) {
3197 fprintf(f, "%d", ss_getpid());
3198 fclose(f);
3199 }
3200#endif
3201
3202 /*---- connect to experiment ----*/
3203 status = cm_connect_experiment1(midas_hostname, midas_expt, prg_name.c_str(), NULL, DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
3205 return 1;
3206 else if (status == DB_INVALID_HANDLE) {
3207 std::string s = cm_get_error(status);
3208 puts(s.c_str());
3209 } else if (status != CM_SUCCESS) {
3210 std::string s = cm_get_error(status);
3211 puts(s.c_str());
3212 return 1;
3213 }
3214
3215 /* check if sequencer already running */
3216 status = cm_exist(prg_name.c_str(), TRUE);
3217 if (status == CM_SUCCESS) {
3218 printf("%s runs already.\n", prg_name.c_str());
3220 return 1;
3221 }
3222
3223 HNDLE hDB;
3225 gOdb = MakeMidasOdb(hDB);
3226
3227 SEQUENCER seq;
3228 SeqCon c;
3229
3230 c.seqp = &seq;
3231 c.hDB = hDB;
3232 c.seq_name = seq_name;
3233 c.prg_name = prg_name;
3234 c.odb_path = odb_path;
3235
3236 init_sequencer(seq, &c);
3237
3238 if (seq_name.empty()) {
3239 printf("Sequencer started. Stop with \"!\"\n");
3240 } else {
3241 printf("%s started.\n", prg_name.c_str());
3242 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
3243 printf("Stop with \"!\"\n");
3244 }
3245
3246 // if any commands are active, process them now
3247 seq_watch_command(hDB, 0, 0, &c);
3248
3249 /* initialize ss_getchar */
3250 ss_getchar(0);
3251
3252 /* main loop */
3253 do {
3254 try {
3255 sequencer(seq, &c);
3256 } catch (std::string &msg) {
3257 seq_error(seq, &c, msg.c_str());
3258 } catch (const char *msg) {
3259 seq_error(seq, &c, msg);
3260 }
3261
3262 status = cm_yield(0);
3263
3264 ch = 0;
3265 while (ss_kbhit()) {
3266 ch = ss_getchar(0);
3267 if (ch == -1)
3268 ch = getchar();
3269
3270 if ((char) ch == '!')
3271 break;
3272 }
3273
3274 } while (status != RPC_SHUTDOWN && ch != '!');
3275
3276 /* reset terminal */
3278
3279 /* close network connection to server */
3281
3282 return 0;
3283}
SEQUENCER * seqp
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_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
INT cm_disconnect_experiment(void)
Definition midas.cxx:2862
INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size)
Definition midas.cxx:2150
INT cm_exist(const char *name, BOOL bUnique)
Definition midas.cxx:7531
#define CM_SUCCESS
Definition midas.h:582
#define CM_WRONG_PASSWORD
Definition midas.h:589
#define DB_INVALID_HANDLE
Definition midas.h:636
#define RPC_SHUTDOWN
Definition midas.h:708
BOOL ss_kbhit()
Definition system.cxx:3736
INT ss_getchar(BOOL reset)
Definition system.cxx:7581
INT ss_getpid(void)
Definition system.cxx:1379
INT ss_daemon_init(BOOL keep_stdout)
Definition system.cxx:2073
std::string cm_get_error(INT code)
Definition midas.cxx:469
void init_sequencer(SEQUENCER &seq, SeqCon *c)
void sequencer(SEQUENCER &seq, SeqCon *c)
BOOL daemon
Definition mana.cxx:258
HNDLE hDB
main ODB handle
Definition mana.cxx:207
#define DEFAULT_WATCHDOG_TIMEOUT
Definition midas.h:290
#define DEFAULT_ODB_SIZE
Definition midas.h:270
Here is the call graph for this function:

◆ msl_parse()

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

Definition at line 508 of file msequencer.cxx.

509 {
510 char str[256], *pl, *pe;
511 char list[100][XNAME_LENGTH], list2[100][XNAME_LENGTH], **lines;
512 int i, j, n, nq, n_lines, endl, line, nest, incl, library;
513 std::string xml, path, fn;
514 char *msl_include, *xml_include, *include_error;
515 int include_error_size;
516 BOOL include_status, rel_path;
517
518 if (level > 10) {
519 snprintf(error, error_size, "More than 10 nested INCLUDE statements exceeded in file \"%s\"", filename);
520 return FALSE;
521 }
522
523 rel_path = (filename[0] != DIR_SEPARATOR);
524 path = std::string(cpath);
525 if (path.length() > 0 && path.back() != '/')
526 path += '/';
527
528 std::string fullFilename;
529 if (rel_path)
530 fullFilename = path + filename;
531 else
532 fullFilename = filename;
533
534 int fhin = open(fullFilename.c_str(), O_RDONLY | O_TEXT);
535 if (fhin < 0) {
536 snprintf(error, error_size, "Cannot open \"%s\", errno %d (%s)", fullFilename.c_str(), errno, strerror(errno));
537 return FALSE;
538 }
539
540 off_t off = lseek(fhin, 0, SEEK_END);
541 if (off < 0) {
542 snprintf(error, error_size, "Cannot find size of \"%s\", lseek(SEEK_END) errno %d (%s)", fullFilename.c_str(), errno, strerror(errno));
543 close(fhin);
544 return FALSE;
545 }
546
547 lseek(fhin, 0, SEEK_SET);
548
549 std::string s;
550 if (rel_path)
551 s = path + xml_filename;
552 else
553 s = xml_filename;
554
555 FILE *fout = fopen(s.c_str(), "wt");
556 if (fout == NULL) {
557 snprintf(error, error_size, "Cannot write to \"%s\", fopen() errno %d (%s)", s.c_str(), errno, strerror(errno));
558 close(fhin);
559 return FALSE;
560 }
561
562 size_t size = off;
563
564 char* buf = (char *) malloc(size + 1);
565 ssize_t rd = read(fhin, buf, size);
566
567 if (rd < 0) {
568 snprintf(error, error_size, "Cannot read \"%s\", read(%zu) errno %d (%s)", fullFilename.c_str(), size, errno, strerror(errno));
569 size = 0;
570 } else {
571 size = rd;
572 }
573
574 buf[size] = 0;
575 close(fhin);
576
577 /* look for any includes */
578 lines = (char **) malloc(sizeof(char *));
579 incl = 0;
580 pl = buf;
581 library = FALSE;
582 for (n_lines = 0; *pl; n_lines++) {
583 lines = (char **) realloc(lines, sizeof(char *) * (n_lines + 1));
584 lines[n_lines] = pl;
585 if (strchr(pl, '\n')) {
586 pe = strchr(pl, '\n');
587 *pe = 0;
588 if (*(pe - 1) == '\r') {
589 *(pe - 1) = 0;
590 }
591 pe++;
592 } else
593 pe = pl + strlen(pl);
594 mstrlcpy(str, pl, sizeof(str));
595 pl = pe;
596 strbreak(str, list, 100, ", ", FALSE);
597 if (equal_ustring(list[0], "include")) {
598 if (!incl) {
599 xml += "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
600 xml += "<!DOCTYPE RunSequence [\n";
601 incl = 1;
602 }
603
604 // if a path is given, use filename as entity reference
605 char *reference = strrchr(list[1], '/');
606 if (reference)
607 reference++;
608 else
609 reference = list[1];
610
611 if (strchr(reference, '.') && !strstr(reference, ".msl") && !strstr(reference, ".MSL")) {
612 snprintf(error, error_size, "Include file \"%s\" cannot have an extension other than .msl", reference);
613 return FALSE;
614 }
615
616 std::string p;
617 if (list[1][0] == '/') {
618 p = std::string(cm_get_path());
619 p += "userfiles/sequencer";
620 p += list[1];
621 } else {
622 p = path;
623 if (!p.empty() && p.back() != '/')
624 p += "/";
625 p += list[1];
626 }
627
628 xml += " <!ENTITY ";
629 xml += reference;
630 xml += " SYSTEM \"";
631 xml += p;
632 xml += ".xml\">\n";
633
634 // recurse
635 size = p.length() + 1 + 4;
636 msl_include = (char *) malloc(size);
637 xml_include = (char *) malloc(size);
638 mstrlcpy(msl_include, p.c_str(), size);
639 mstrlcpy(xml_include, p.c_str(), size);
640 if (!strstr(msl_include, ".msl"))
641 mstrlcat(msl_include, ".msl", size);
642 mstrlcat(xml_include, ".xml", size);
643
644 include_error = error + strlen(error);
645 include_error_size = error_size - strlen(error);
646
647 std::string path(cm_get_path());
648 path += "userfiles/sequencer/";
649 path += seq.path;
650
651 include_status = msl_parse(seq, c, level+1, path.c_str(), msl_include, xml_include, include_error, include_error_size, error_line);
652 free(msl_include);
653 free(xml_include);
654
655 if (!include_status) {
656 // report the error on CALL line instead of the one in included file
657 *error_line = n_lines + 1;
658 return FALSE;
659 }
660 }
661 if (equal_ustring(list[0], "library")) {
662 xml += "<Library name=\"";
663 xml += list[1];
664 xml += "\">\n";
665 library = TRUE;
666 }
667 }
668 if (incl) {
669 xml += "]>\n";
670 } else if (!library) {
671 xml += "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
672 }
673
674 /* parse rest of file */
675 if (!library) {
676 xml += "<RunSequence xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"\">\n";
677 }
678
679 std::vector<std::string> slines;
680 for (line = 0; line < n_lines; line++) {
681 slines.push_back(lines[line]);
682 }
683
684 c->odbs->WSA("Script/Lines", slines, 0);
685
686 /* save parameters */
687 std::string p = "/" + c->odb_path + "/Param/Value";
688 midas::odb oldSeqParam(p);
689 oldSeqParam.set_auto_refresh_read(false);
690 oldSeqParam.set_auto_create(false);
691
692 /* clear all variables */
693 if (!seq.running) {
694 c->odbs->Delete("Variables");
695 c->odbs->Delete("Param");
696 }
697
698 for (line = 0; line < n_lines; line++) {
699 char *p = lines[line];
700 while (*p == ' ')
701 p++;
702 mstrlcpy(list[0], p, sizeof(list[0]));
703 if (strchr(list[0], ' '))
704 *strchr(list[0], ' ') = 0;
705 p += strlen(list[0]);
706 n = strbreak(p + 1, &list[1], 99, ",", FALSE) + 1;
707
708 /* remove any full comment line */
709 for (i = 0; i < n; i++) {
710 if (list[i][0] == '#') {
711 for (j = i; j < n; j++)
712 list[j][0] = 0;
713 break;
714 }
715 }
716
717 /* cut any partial comment line */
718 for (i = 0; i < n; i++) {
719 if (strchr(list[i], '#'))
720 *strchr(list[i], '#') = 0;
721 }
722
723 /* strip any trailing blanks */
724 for (i = 0; i < n; i++) {
725 while (strlen(list[i]) > 0 && list[i][strlen(list[i])-1] == ' ')
726 list[i][strlen(list[i])-1] = 0;
727 }
728
729 /* check for variable assignment */
730 char eq[1024];
731 mstrlcpy(eq, lines[line], sizeof(eq));
732 if (strchr(eq, '#'))
733 *strchr(eq, '#') = 0;
734 for (i = 0, n = 0, nq = 0; i < (int)strlen(eq); i++) {
735 if (eq[i] == '\"')
736 nq = (nq == 0 ? 1 : 0);
737 if (eq[i] == '=' && nq == 0 &&
738 (i > 0 && (eq[i - 1] != '!') && eq[i - 1] != '<' && eq[i - 1] != '>'))
739 n++;
740 }
741 if (n == 1 && eq[0] != '=') {
742 // equation found
743 mstrlcpy(list[0], "SET", sizeof(list[0]));
744 p = eq;
745 while (*p == ' ')
746 p++;
747 mstrlcpy(list[1], p, sizeof(list[1]));
748 *strchr(list[1], '=') = 0;
749 if (strchr(list[1], ' '))
750 *strchr(list[1], ' ') = 0;
751 p = strchr(eq, '=')+1;
752 while (*p == ' ')
753 p++;
754 mstrlcpy(list[2], p, sizeof(list[2]));
755 while (strlen(list[2]) > 0 && list[2][strlen(list[2])-1] == ' ')
756 list[2][strlen(list[2])-1] = 0;
757 }
758
759 if (equal_ustring(list[0], "library")) {
760
761 } else if (equal_ustring(list[0], "include")) {
762 // if a path is given, use filename as entity reference
763 char *reference = strrchr(list[1], '/');
764 if (reference)
765 reference++;
766 else
767 reference = list[1];
768
769 xml += "&";
770 xml += reference;
771 xml += ";\n";
772
773 } else if (equal_ustring(list[0], "call")) {
774 xml += "<Call " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + ">";
775 for (i = 2; i < 100 && list[i][0]; i++) {
776 if (i > 2) {
777 xml += ",";
778 }
779 xml += list[i];
780 }
781 xml += "</Call>\n";
782
783 } else if (equal_ustring(list[0], "cat")) {
784 xml += "<Cat " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + ">";
785 for (i = 2; i < 100 && list[i][0]; i++) {
786 if (i > 2) {
787 xml += ",";
788 }
789 xml += q(list[i]);
790 }
791 xml += "</Cat>\n";
792
793 } else if (equal_ustring(list[0], "comment")) {
794 xml += "<Comment " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</Comment>\n";
795
796 } else if (equal_ustring(list[0], "exit")) {
797 xml += "<Exit " + qtoString(fullFilename, line + 1, level) + " />\n";
798
799 } else if (equal_ustring(list[0], "goto")) {
800 xml += "<Goto " + qtoString(fullFilename, line + 1, level) + " sline=" + q(list[1]) + " />\n";
801
802 } else if (equal_ustring(list[0], "if")) {
803 xml += "<If " + qtoString(fullFilename, line + 1, level) + " condition=\"";
804 for (i = 1; i < 100 && list[i][0] && stricmp(list[i], "THEN") != 0; i++) {
805 xml += list[i];
806 }
807 xml += "\">\n";
808
809 } else if (equal_ustring(list[0], "else")) {
810 xml += "<Else />\n";
811
812 } else if (equal_ustring(list[0], "endif")) {
813 xml += "</If>\n";
814
815 } else if (equal_ustring(list[0], "loop")) {
816 /* find end of loop */
817 for (i = line, nest = 0; i < n_lines; i++) {
818 strbreak(lines[i], list2, 100, ", ", FALSE);
819 if (equal_ustring(list2[0], "loop"))
820 nest++;
821 if (equal_ustring(list2[0], "endloop")) {
822 nest--;
823 if (nest == 0)
824 break;
825 }
826 }
827 if (i < n_lines)
828 endl = i + 1;
829 else
830 endl = line + 1;
831 if (list[2][0] == 0) {
832 xml += "<Loop " + qtoString(fullFilename, line + 1, level) + " le=\"" + std::to_string(endl) + "\" n=" + q(list[1]) + ">\n";
833 } else if (list[3][0] == 0) {
834 xml += "<Loop " + qtoString(fullFilename, line + 1, level) + " le=\"" + std::to_string(endl) + "\" var=" + q(list[1]) + " n=" +
835 q(list[2]) + ">\n";
836 } else {
837 xml += "<Loop " + qtoString(fullFilename, line + 1, level) + " le=\"" + std::to_string(endl) + "\" var=" + q(list[1]) + " values=\"";
838 for (i = 2; i < 100 && list[i][0]; i++) {
839 if (i > 2) {
840 xml += ",";
841 }
842 xml += list[i];
843 }
844 xml += "\">\n";
845 }
846 } else if (equal_ustring(list[0], "endloop")) {
847 xml += "</Loop>\n";
848
849 } else if (equal_ustring(list[0], "break")) {
850 xml += "<Break "+ qtoString(fullFilename, line + 1, level) +"></Break>\n";
851
852 } else if (equal_ustring(list[0], "message")) {
853 xml += "<Message " + qtoString(fullFilename, line + 1, level);
854 if (list[2][0] == '1')
855 xml += " wait=\"1\"";
856 xml += ">";
857 xml += list[1];
858 xml += "</Message>\n";
859
860 } else if (equal_ustring(list[0], "msg")) {
861 if (list[2][0]) {
862 xml += "<Msg " + qtoString(fullFilename, line + 1, level) + " type=\""+list[2]+"\">" + list[1] + "</Msg>\n";
863 } else {
864 xml += "<Msg " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</Msg>\n";
865 }
866
867 } else if (equal_ustring(list[0], "odbinc")) {
868 if (list[2][0] == 0)
869 mstrlcpy(list[2], "1", 2);
870 xml += "<ODBInc " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">" + list[2] + "</ODBInc>\n";
871
872 } else if (equal_ustring(list[0], "odbcreate")) {
873 if (list[3][0]) {
874 xml += "<ODBCreate " + qtoString(fullFilename, line + 1, level) + " size=" + q(list[3]) + " path=" + q(list[1]) + " type=" +
875 q(list[2]) + "></ODBCreate>\n";
876 } else {
877 xml += "<ODBCreate " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + " type=" + q(list[2]) +
878 "></ODBCreate>\n";
879 }
880
881 } else if (equal_ustring(list[0], "odbdelete")) {
882 xml += "<ODBDelete " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</ODBDelete>\n";
883
884 } else if (equal_ustring(list[0], "odbset")) {
885 if (list[3][0]) {
886 xml += "<ODBSet " + qtoString(fullFilename, line + 1, level) + " notify=" + q(list[3]) + " path=" + q(list[1]) + ">" +
887 list[2] + "</ODBSet>\n";
888 } else {
889 xml += "<ODBSet " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">" + list[2] + "</ODBSet>\n";
890 }
891
892 } else if (equal_ustring(list[0], "odbload")) {
893 if (list[2][0]) {
894 xml += "<ODBLoad " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[2]) + ">" + list[1] + "</ODBLoad>\n";
895 } else {
896 xml += "<ODBLoad " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</ODBLoad>\n";
897 }
898
899 } else if (equal_ustring(list[0], "odbget")) {
900 xml += "<ODBGet " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">" + list[2] + "</ODBGet>\n";
901
902 } else if (equal_ustring(list[0], "odblookup")) {
903 xml += "<ODBLookup " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) +
904 " string=" + q(list[2]) + ">" + list[3] + "</ODBLookup>\n";
905
906 } else if (equal_ustring(list[0], "odbsave")) {
907 xml += "<ODBSave " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">" + list[2] + "</ODBSave>\n";
908
909 } else if (equal_ustring(list[0], "odbsubdir")) {
910 if (list[2][0]) {
911 xml += "<ODBSubdir " + qtoString(fullFilename, line + 1, level) + " notify=" + q(list[2]) + " path=" + q(list[1]) + ">\n";
912 } else {
913 xml += "<ODBSubdir " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">\n";
914 }
915 } else if (equal_ustring(list[0], "endodbsubdir")) {
916 xml += "</ODBSubdir>\n";
917
918 } else if (equal_ustring(list[0], "param")) {
919 if (list[2][0] == 0 || equal_ustring(list[2], "bool")) { // name and bool
920 snprintf(error, error_size, "Parameter \"%s\" misses 'comment'", list[1]);
921 *error_line = line + 1;
922 return FALSE;
923
924 } else if (!list[4][0] && equal_ustring(list[3], "bool")) { // name, comment and bool
925 xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " type=\"bool\" " + " comment=" + q(list[2]) + "/>\n";
926 bool v = false;
927 c->odbs->RB((std::string("Param/Value/") + list[1]).c_str(), &v, true);
928 c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
929 } else if (!list[5][0] && equal_ustring(list[4], "bool")) { // name, comment, default and bool
930 xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " type=\"bool\" default=" + q(list[4]) +"/>\n";
931 std::string def(list[3]);
932 bool v(def == "1" || def == "true" || def == "TRUE");
933 c->odbs->RB((std::string("Param/Value/") + list[1]).c_str(), &v, true);
934 c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
935 c->odbs->WS((std::string("Param/Defaults/") + list[1]).c_str(), list[3]);
936
937 } else if (!list[3][0]) { // name and comment
938 xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " comment=" + q(list[2]) + " />\n";
939 std::string v;
940 c->odbs->RS((std::string("Param/Value/") + list[1]).c_str(), &v, true);
941 c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
942 } else if (!list[4][0]) { // name, comment and default
943 xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " comment=" + q(list[2]) + " default=" + q(list[3]) + " />\n";
944 std::string v(list[3]);
945 c->odbs->RS((std::string("Param/Value/") + list[1]).c_str(), &v, true);
946 c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
947 c->odbs->WS((std::string("Param/Defaults/") + list[1]).c_str(), list[3]);
948 // ss_sleep(1000); // use this for parameter test
949 } else {
950 xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " comment=" + q(list[2]) +
951 " options=\"";
952 std::string v;
953 c->odbs->RS((std::string("Param/Value/") + list[1]).c_str(), &v, true);
954 c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
955 std::vector<std::string> options;
956 for (i = 3; i < 100 && list[i][0]; i++) {
957 if (i > 3) {
958 xml += ",";
959 }
960 xml += list[i];
961 options.push_back(list[i]);
962 }
963 xml += "\" />\n";
964 c->odbs->WSA((std::string("Param/Options/") + list[1]).c_str(), options, 0);
965 }
966
967 // put back old parameter value if existing
968 try {
969 std::string ov = oldSeqParam[(const char *)list[1]];
970 c->odbs->WS((std::string("Param/Value/") + list[1]).c_str(), ov.c_str());
971 } catch(mexception &e) {}
972
973 } else if (equal_ustring(list[0], "rundescription")) {
974 xml += "<RunDescription " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</RunDescription>\n";
975
976 } else if (equal_ustring(list[0], "script")) {
977 if (list[2][0] == 0) {
978 xml += "<Script " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</Script>\n";
979 } else {
980 xml += "<Script " + qtoString(fullFilename, line + 1, level) + " params=\"";
981 for (i = 2; i < 100 && list[i][0]; i++) {
982 if (i > 2) {
983 xml += ",";
984 }
985 xml += list[i];
986 }
987 xml += "\">";
988 xml += list[1];
989 xml += "</Script>\n";
990 }
991
992 } else if (equal_ustring(list[0], "set")) {
993 xml += "<Set " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + ">" + list[2] + "</Set>\n";
994
995 } else if (equal_ustring(list[0], "subroutine")) {
996 xml += "\n<Subroutine " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + ">\n";
997
998 } else if (equal_ustring(list[0], "endsubroutine")) {
999 xml += "</Subroutine>\n";
1000
1001 } else if (equal_ustring(list[0], "transition")) {
1002 xml += "<Transition " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</Transition>\n";
1003
1004 } else if (equal_ustring(list[0], "wait")) {
1005 if (!list[2][0]) {
1006 xml += "<Wait " + qtoString(fullFilename, line + 1, level) + " for=\"seconds\">" + list[1] + "</Wait>\n";
1007 } else if (!list[3][0]) {
1008 xml += "<Wait " + qtoString(fullFilename, line + 1, level) + " for=" + q(list[1]) + ">" + list[2] + "</Wait>\n";
1009 } else {
1010 xml += "<Wait " + qtoString(fullFilename, line + 1, level) + " for=" + q(list[1]) + " path=" + q(list[2]) + " op=" +
1011 q(list[3]);
1012
1013 // Optional timeout for ODBValue waits:
1014 // WAIT ODBValue, path, op, value, timeout, seconds[, status_variable]
1015 // Store the timeout in the XML node. The runtime publishes progress
1016 // through State/Timeout value and State/Timeout limit. If a status
1017 // variable name is given, the runtime writes Variables/<status_variable>:
1018 // 1 = condition satisfied before timeout
1019 // 0 = timeout expired before condition was satisfied
1020 if (list[5][0]) {
1021 if (!equal_ustring(list[1], "ODBValue")) {
1022 snprintf(error, error_size, "WAIT timeout is only supported for WAIT ODBValue");
1023 *error_line = line + 1;
1024 return FALSE;
1025 }
1026 if (!equal_ustring(list[5], "timeout") || !list[6][0] || list[8][0]) {
1027 snprintf(error, error_size,
1028 "Invalid WAIT ODBValue timeout syntax. Use: WAIT ODBValue, path, op, value, timeout, seconds[, status_variable]");
1029 *error_line = line + 1;
1030 return FALSE;
1031 }
1032 xml += " timeout=" + q(list[6]);
1033 if (list[7][0])
1034 xml += " status=" + q(list[7]);
1035 }
1036
1037 xml += ">" + std::string(list[4]) + "</Wait>\n";
1038 }
1039
1040 } else if (list[0][0] == 0 || list[0][0] == '#') {
1041 /* skip empty or out-commented lines */
1042 } else {
1043 snprintf(error, error_size, "Invalid command \"%s\"", list[0]);
1044 *error_line = line + 1;
1045 return FALSE;
1046 }
1047 }
1048
1049 free(lines);
1050 free(buf);
1051 if (library) {
1052 xml += "\n</Library>\n";
1053 } else {
1054 xml += "</RunSequence>\n";
1055 }
1056
1057 // write XML to .xml file
1058 fprintf(fout, "%s", xml.c_str());
1059 fclose(fout);
1060
1061 return TRUE;
1062}
std::string cm_get_path()
Definition midas.cxx:1553
#define O_TEXT
Definition msystem.h:227
static std::string qtoString(std::string filename, int line, int level)
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 std::string q(const char *s)
std::vector< FMT_ID > eq
Definition mdump.cxx:55
DWORD BOOL
Definition midas.h:105
#define read(n, a, f)
INT j
Definition odbhist.cxx:40
BOOL running
Definition sequencer.h:18
static double e(void)
Definition tinyexpr.c:136
Here is the call graph for this function:
Here is the caller graph for this function:

◆ q()

static std::string q ( const char *  s)
static

Definition at line 141 of file msequencer.cxx.

141 {
142 return "\"" + std::string(s) + "\"";
143}
Here is the caller graph for this function:

◆ qtoString()

static std::string qtoString ( std::string  filename,
int  line,
int  level 
)
static

Definition at line 133 of file msequencer.cxx.

133 {
134 std::string buf = "l=\"" + std::to_string(line) + "\"";
135 buf += " fn=\"" + filename + "\"";
136 if (level)
137 buf += " lvl=\"" + std::to_string(level) + "\"";
138 return buf;
139}
Here is the caller graph for this function:

◆ seq_array_index()

void seq_array_index ( SEQUENCER seq,
SeqCon c,
char *  odbpath,
int *  index1,
int *  index2 
)

Definition at line 1644 of file msequencer.cxx.

1644 {
1645 char str[256];
1646 *index1 = *index2 = 0;
1647 if (odbpath[strlen(odbpath) - 1] == ']') {
1648 if (strchr(odbpath, '[')) {
1649 //check for sequencer variables
1650 if (strchr((strchr(odbpath, '[') + 1), '$')) {
1651 mstrlcpy(str, strchr(odbpath, '[') + 1, sizeof(str));
1652 if (strchr(str, ']'))
1653 *strchr(str, ']') = 0;
1654 *index1 = atoi(eval_var(seq, c, str).c_str());
1655
1656 *strchr(odbpath, '[') = 0;
1657 } else {
1658 //standard expansion
1659 strarrayindex(odbpath, index1, index2);
1660 }
1661 }
1662 }
1663}
void strarrayindex(char *odbpath, int *index1, int *index2)
Definition odb.cxx:3357
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_cancel_stop_after_run()

static void seq_cancel_stop_after_run ( SEQUENCER seq,
SeqCon c 
)
static

Definition at line 1223 of file msequencer.cxx.

1223 {
1224 seq_read(&seq, c);
1225 seq.stop_after_run = false;
1226 seq_write(seq, c);
1227}
BOOL stop_after_run
Definition sequencer.h:25
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_clear()

void seq_clear ( SEQUENCER seq)

Definition at line 1102 of file msequencer.cxx.

1102 {
1103 seq.running = FALSE;
1104 seq.finished = FALSE;
1105 seq.paused = FALSE;
1107 seq.wait_limit = 0;
1108 seq.wait_value = 0;
1109 seq.timeout_limit = 0;
1110 seq.timeout_value = 0;
1111 seq.start_time = 0;
1112 seq.wait_type[0] = 0;
1113 seq.wait_odb[0] = 0;
1114 for (int i = 0; i < SEQ_NEST_LEVEL_LOOP; i++) {
1115 seq.loop_start_line[i] = 0;
1116 seq.sloop_start_line[i] = 0;
1117 seq.loop_end_line[i] = 0;
1118 seq.sloop_end_line[i] = 0;
1119 seq.loop_counter[i] = 0;
1120 seq.loop_n[i] = 0;
1121 }
1122 for (int i = 0; i < SEQ_NEST_LEVEL_IF; i++) {
1123 seq.if_else_line[i] = 0;
1124 seq.if_endif_line[i] = 0;
1125 }
1126 for (int i = 0; i < SEQ_NEST_LEVEL_SUB; i++) {
1127 seq.subroutine_end_line[i] = 0;
1128 seq.subroutine_return_line[i] = 0;
1129 seq.subroutine_call_line[i] = 0;
1130 seq.ssubroutine_call_line[i] = 0;
1131 seq.subroutine_param[i][0] = 0;
1132 }
1133 seq.current_line_number = 0;
1134 seq.scurrent_line_number = 0;
1135 seq.sfilename[0] = 0;
1136 seq.if_index = 0;
1137 seq.stack_index = 0;
1138 seq.error[0] = 0;
1139 seq.error_line = 0;
1140 seq.serror_line = 0;
1141 seq.subdir[0] = 0;
1142 seq.subdir_end_line = 0;
1143 seq.subdir_not_notify = 0;
1144 seq.message[0] = 0;
1145 seq.message_wait = FALSE;
1146 seq.stop_after_run = FALSE;
1147}
#define SEQ_NEST_LEVEL_IF
Definition sequencer.h:4
#define SEQ_NEST_LEVEL_LOOP
Definition sequencer.h:3
int serror_line
Definition sequencer.h:15
char wait_odb[256]
Definition sequencer.h:52
int if_index
Definition sequencer.h:36
float wait_value
Definition sequencer.h:46
int loop_end_line[SEQ_NEST_LEVEL_LOOP]
Definition sequencer.h:29
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
BOOL message_wait
Definition sequencer.h:17
int loop_start_line[SEQ_NEST_LEVEL_LOOP]
Definition sequencer.h:27
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
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
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 subdir_end_line
Definition sequencer.h:34
int loop_counter[SEQ_NEST_LEVEL_LOOP]
Definition sequencer.h:31
char error[256]
Definition sequencer.h:13
float timeout_limit
Definition sequencer.h:49
int scurrent_line_number
Definition sequencer.h:23
char wait_type[32]
Definition sequencer.h:51
char sfilename[256]
Definition sequencer.h:11
int subdir_not_notify
Definition sequencer.h:35
Here is the caller graph for this function:

◆ seq_clear_wait_state()

static void seq_clear_wait_state ( SEQUENCER seq)
static

Definition at line 1382 of file msequencer.cxx.

1382 {
1383 seq.start_time = 0;
1384 seq.wait_limit = 0;
1385 seq.wait_value = 0;
1386 seq.timeout_limit = 0;
1387 seq.timeout_value = 0;
1388 seq.wait_type[0] = 0;
1389 seq.wait_odb[0] = 0;
1390}
Here is the caller graph for this function:

◆ seq_error()

void seq_error ( SEQUENCER seq,
SeqCon c,
const char *  str 
)

Definition at line 164 of file msequencer.cxx.

164 {
165 int status;
166 HNDLE hKey;
167
168 mstrlcpy(seq.error, str, sizeof(seq.error));
171 seq.running = FALSE;
173
174 status = db_find_key(c->hDB, c->hSeq, "State", &hKey);
175 if (status != DB_SUCCESS)
176 return;
177 status = db_set_record(c->hDB, hKey, &seq, sizeof(seq), 0);
178 if (status != DB_SUCCESS)
179 return;
180
181 cm_msg(MTALK, "sequencer", "Sequencer has stopped with error.");
182}
#define MTALK
Definition midas.h:564
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_goto_context()

static std::string seq_goto_context ( SeqCon c,
int  line 
)
static

Definition at line 1394 of file msequencer.cxx.

1394 {
1395 std::string context;
1396 PMXML_NODE pr = mxml_find_node(c->pnseq, "RunSequence");
1397
1398 if (!pr)
1399 return context;
1400
1401 for (int i = 1; i <= mxml_get_line_number_end(pr); i++) {
1402 PMXML_NODE pt = mxml_get_node_at_line(c->pnseq, i);
1403 if (!pt)
1404 continue;
1405
1406 const char *name = mxml_get_name(pt);
1407 if (!equal_ustring(name, "Loop") && !equal_ustring(name, "If") && !equal_ustring(name, "Subroutine"))
1408 continue;
1409
1410 int start_line = mxml_get_line_number_start(pt);
1411 int end_line = mxml_get_line_number_end(pt);
1412
1413 // The block opener itself is not considered inside the block. Jumping
1414 // to a LOOP/IF/SUBROUTINE line is allowed only if the surrounding
1415 // context matches; the normal statement handler will initialize or skip
1416 // the block state.
1417 if (line > start_line && line < end_line)
1418 context += msprintf("%s:%d:%d;", name, start_line, end_line);
1419 }
1420
1421 return context;
1422}
std::string msprintf(const char *format,...)
Definition midas.cxx:419
#define name(x)
Definition midas_macro.h:24
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_goto_line()

static BOOL seq_goto_line ( SEQUENCER seq,
SeqCon c,
int  target_sline 
)
static

Definition at line 1426 of file msequencer.cxx.

1426 {
1427 PMXML_NODE pr, pt;
1428 int target_line = 0;
1429 int target_sline_found = 0;
1430 int best_sline = 0;
1431
1432 if (target_sline <= 0) {
1433 cm_msg(MERROR, "sequencer", "Goto line command ignored: invalid line number %d", target_sline);
1434 return FALSE;
1435 }
1436
1437 if (c->pnseq == NULL) {
1438 cm_msg(MERROR, "sequencer", "Goto line command ignored: no script loaded");
1439 return FALSE;
1440 }
1441
1442 pr = mxml_find_node(c->pnseq, "RunSequence");
1443 if (!pr) {
1444 cm_msg(MERROR, "sequencer", "Goto line command ignored: cannot find <RunSequence> tag in XML file");
1445 return FALSE;
1446 }
1447
1448 // Find the first executable XML node generated at or after the requested
1449 // source line. This lets "Goto line" work when the requested source line is
1450 // a comment or blank line.
1451 for (int i = 1; i <= mxml_get_line_number_end(pr); i++) {
1452 pt = mxml_get_node_at_line(c->pnseq, i);
1453 if (pt && mxml_get_attribute(pt, "l")) {
1454 int sline = atoi(mxml_get_attribute(pt, "l"));
1455
1456 if (sline >= target_sline && (best_sline == 0 || sline < best_sline)) {
1457 best_sline = sline;
1458 target_sline_found = sline;
1459 target_line = i;
1460 }
1461 }
1462 }
1463
1464 if (target_line == 0) {
1465 cm_msg(MERROR, "sequencer",
1466 "Goto line %d failed: no executable sequencer command found at or after this source line",
1467 target_sline);
1468 return FALSE;
1469 }
1470
1471 seq_read(&seq, c);
1472
1473 if (!seq.running) {
1474 cm_msg(MERROR, "sequencer", "Goto line command ignored: sequencer is not running");
1475 return FALSE;
1476 }
1477
1478 std::string current_context = seq_goto_context(c, seq.current_line_number);
1479 std::string target_context = seq_goto_context(c, target_line);
1480
1481 // Phase two: do not allow jumps into or out of LOOP/IF/SUBROUTINE blocks.
1482 // This prevents stale loop counters, IF bookkeeping, or subroutine return
1483 // stack state. A later phase could allow jump-out by explicitly unwinding
1484 // the affected state.
1485 if (current_context != target_context) {
1486 cm_msg(MERROR, "sequencer",
1487 "Goto line command ignored: source line %d resolves to line %d, which is in a different LOOP/IF/SUBROUTINE context",
1488 target_sline, target_sline_found);
1489 return FALSE;
1490 }
1491
1492 cm_msg(MINFO, "sequencer",
1493 "Goto line command: moving from source line %d to source line %d.",
1494 seq.scurrent_line_number, target_sline_found);
1495
1497 seq.current_line_number = target_line;
1498 seq.scurrent_line_number = target_sline_found;
1499
1500 seq_write(seq, c);
1501
1502 return TRUE;
1503}
#define MINFO
Definition midas.h:560
static void seq_clear_wait_state(SEQUENCER &seq)
static std::string seq_goto_context(SeqCon *c, int line)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_open_file()

static void seq_open_file ( const char *  str,
SEQUENCER seq,
SeqCon c 
)
static

Definition at line 1293 of file msequencer.cxx.

1293 {
1294 seq.new_file = FALSE;
1295 seq.error[0] = 0;
1296 seq.error_line = 0;
1297 seq.serror_line = 0;
1298 if (c->pnseq) {
1299 mxml_free_tree(c->pnseq);
1300 c->pnseq = NULL;
1301 }
1302 c->odbs->WS("Script/Lines", "");
1303
1304 if (stristr(str, ".msl")) {
1305 int size = (int)strlen(str) + 1;
1306 char *xml_filename = (char *) malloc(size);
1307 mstrlcpy(xml_filename, str, size);
1308 strsubst(xml_filename, size, ".msl", ".xml");
1309 //printf("Parsing MSL sequencer file: %s to XML sequencer file %s\n", str, xml_filename);
1310
1311 std::string path(cm_get_path());
1312 path += "userfiles/sequencer/";
1313
1314 if (xml_filename[0] == '/') {
1315 path += xml_filename;
1316 path = path.substr(0, path.find_last_of('/') + 1);
1317 mstrlcpy(xml_filename, path.substr(path.find_last_of('/') + 1).c_str(), size);
1318 } else {
1319 path += seq.path;
1320 }
1321
1322 if (msl_parse(seq, c, 0, path.c_str(), str, xml_filename, seq.error, sizeof(seq.error), &seq.serror_line)) {
1323 //printf("Loading XML sequencer file: %s\n", xml_filename);
1324 std::string fn(path);
1325 if (!fn.empty() && fn.back() != '/')
1326 fn += '/';
1327 fn += xml_filename;
1328 c->pnseq = mxml_parse_file(fn.c_str(), seq.error, sizeof(seq.error), &seq.error_line);
1329 } else {
1330 //printf("Error in MSL sequencer file \"%s\" line %d, error: %s\n", str, seq.serror_line, seq.error);
1331 }
1332 free(xml_filename);
1333 } else {
1334 //printf("Loading XML sequencer file: %s\n", str);
1335 c->pnseq = mxml_parse_file(str, seq.error, sizeof(seq.error), &seq.error_line);
1336 }
1337}
void strsubst(char *string, int size, const char *pattern, const char *subst)
char * stristr(const char *str, const char *pattern)
BOOL new_file
Definition sequencer.h:8
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_pause()

static void seq_pause ( SEQUENCER seq,
SeqCon c,
bool  flag 
)
static

Definition at line 1231 of file msequencer.cxx.

1231 {
1232 seq_read(&seq, c);
1233 seq.paused = flag;
1234 if (!flag)
1235 seq.debug = false;
1236 seq_write(seq, c);
1237}
BOOL debug
Definition sequencer.h:21
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_read()

void seq_read ( SEQUENCER seq,
SeqCon c 
)

Definition at line 1066 of file msequencer.cxx.

1066 {
1067 int status;
1068 HNDLE hKey;
1069
1070 status = db_find_key(c->hDB, c->hSeq, "State", &hKey);
1071 if (status != DB_SUCCESS) {
1072 cm_msg(MERROR, "seq_read", "Cannot find /Sequencer/State in ODB, db_find_key() status %d", status);
1073 return;
1074 }
1075
1076 int size = sizeof(SEQUENCER);
1077 status = db_get_record1(c->hDB, hKey, seq, &size, 0, strcomb1(sequencer_str).c_str());
1078 if (status != DB_SUCCESS) {
1079 cm_msg(MERROR, "seq_read", "Cannot get /Sequencer/State from ODB, db_get_record1() status %d", status);
1080 return;
1081 }
1082}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_start()

static void seq_start ( SEQUENCER seq,
SeqCon c,
bool  debug 
)
static

Definition at line 1151 of file msequencer.cxx.

1151 {
1152 seq_read(&seq, c);
1153 seq_clear(seq);
1154
1155 // manage sequencer parameters and variables
1156
1157 midas::odb defaults(std::string("/") + c->odb_path + "/Param/Defaults");
1158 midas::odb param(std::string("/") + c->odb_path + "/Param/Value");
1159 midas::odb vars(std::string("/") + c->odb_path + "/Variables");
1160
1161 // check if /Param/Value is there
1162 for (midas::odb& d: defaults)
1163 if (!param.is_subkey(d.get_name())) {
1164 mstrlcpy(seq.error, "Cannot start script because /Sequencer/Param/Value is incomplete", sizeof(seq.error));
1165 seq_write(seq, c);
1166 cm_msg(MERROR, "sequencer", "Cannot start script because /Sequencer/Param/Value is incomplete");
1167 return;
1168 }
1169
1170 // copy /Sequencer/Param/Value to /Sequencer/Variables
1171 for (midas::odb& p: param)
1172 vars[p.get_name()] = p.s();
1173
1174 if (!c->pnseq) {
1175 mstrlcpy(seq.error, "Cannot start script, no script loaded", sizeof(seq.error));
1176 seq_write(seq, c);
1177 return;
1178 }
1179
1180 // start sequencer
1181 seq.running = TRUE;
1182 seq.debug = debug;
1183 seq.current_line_number = 1;
1184 seq.scurrent_line_number = 1;
1185 mstrlcpy(seq.sfilename, seq.filename, sizeof(seq.sfilename));
1186 seq_write(seq, c);
1187
1188 if (debug)
1189 cm_msg(MTALK, "sequencer", "Sequencer started with script \"%s\" in debugging mode.", seq.filename);
1190 else
1191 cm_msg(MTALK, "sequencer", "Sequencer started with script \"%s\".", seq.filename);
1192}
void seq_clear(SEQUENCER &seq)
BOOL debug
debug printouts
Definition mana.cxx:254
double d
Definition system.cxx:1313
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_start_next()

static void seq_start_next ( SEQUENCER seq,
SeqCon c 
)
static

Definition at line 1341 of file msequencer.cxx.

1341 {
1342 seq_read(&seq, c);
1343 if (seq.next_filename[0][0]) {
1344 mstrlcpy(seq.filename, seq.next_filename[0], sizeof(seq.filename));
1345 for (int i=0 ; i<9 ; i++)
1346 mstrlcpy(seq.next_filename[i], seq.next_filename[i+1], 256);
1347 seq.next_filename[9][0] = 0;
1348 seq_write(seq, c);
1349
1350 seq_open_file(seq.filename, seq, c);
1351
1352 seq_start(seq, c, false);
1353 }
1354}
static void seq_start(SEQUENCER &seq, SeqCon *c, bool debug)
char next_filename[10][256]
Definition sequencer.h:12
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_step_over()

static void seq_step_over ( SEQUENCER seq,
SeqCon c 
)
static

Definition at line 1284 of file msequencer.cxx.

1284 {
1285 seq_read(&seq, c);
1286 seq.paused = false;
1287 seq.debug = true;
1288 seq_write(seq, c);
1289}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_stop()

static void seq_stop ( SEQUENCER seq,
SeqCon c 
)
static

Definition at line 1196 of file msequencer.cxx.

1196 {
1197 seq_read(&seq, c);
1198
1199 if (seq.follow_libraries) {
1200 std::string path(cm_get_path());
1201 path += "userfiles/sequencer/";
1202 path += seq.path;
1203 path += seq.filename;
1204
1205 loadMSL(c->odbs, path);
1206 }
1207
1208 seq_clear(seq);
1209 seq.finished = TRUE;
1210 seq_write(seq, c);
1211}
bool loadMSL(MVOdb *o, const std::string &filename)
int follow_libraries
Definition sequencer.h:24
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_stop_after_run()

static void seq_stop_after_run ( SEQUENCER seq,
SeqCon c 
)
static

Definition at line 1215 of file msequencer.cxx.

1215 {
1216 seq_read(&seq, c);
1217 seq.stop_after_run = true;
1218 seq_write(seq, c);
1219}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_watch()

static void seq_watch ( HNDLE  hDB,
HNDLE  hKeyChanged,
int  index,
void *  info 
)
static

Definition at line 1358 of file msequencer.cxx.

1358 {
1359 char str[256];
1360 SeqCon* c = (SeqCon*)info;
1361
1362 seq_read(c->seqp, c);
1363
1364 if (c->seqp->new_file) {
1365 mstrlcpy(str, c->seqp->path, sizeof(str));
1366 if (strlen(str) > 1 && str[strlen(str) - 1] != DIR_SEPARATOR)
1367 mstrlcat(str, DIR_SEPARATOR_STR, sizeof(str));
1368 mstrlcat(str, c->seqp->filename, sizeof(str));
1369
1370 //printf("Load file %s\n", str);
1371
1372 seq_open_file(str, *(c->seqp), c);
1373
1374 seq_clear(*(c->seqp));
1375
1376 seq_write(*(c->seqp), c);
1377 }
1378}
void ** info
Definition fesimdaq.cxx:41
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_watch_command()

static void seq_watch_command ( HNDLE  hDB,
HNDLE  hKeyChanged,
int  index,
void *  info 
)
static

Definition at line 1506 of file msequencer.cxx.

1506 {
1507 SeqCon* c = (SeqCon*)info;
1508 SEQUENCER* seqp = c->seqp;
1509
1510 bool start_script = false;
1511 bool stop_immediately = false;
1512 bool stop_after_run = false;
1513 bool cancel_stop_after_run = false;
1514 bool pause_script = false;
1515 bool resume_script = false;
1516 bool debug_script = false;
1517 bool step_over = false;
1518 bool load_new_file = false;
1519 int goto_line = 0;
1520 int goto_line_size = sizeof(goto_line);
1521
1522 c->odbs->RB("Command/Start script", &start_script);
1523 c->odbs->RB("Command/Stop immediately", &stop_immediately);
1524 c->odbs->RB("Command/Stop after run", &stop_after_run);
1525 c->odbs->RB("Command/Cancel stop after run", &cancel_stop_after_run);
1526 c->odbs->RB("Command/Pause script", &pause_script);
1527 c->odbs->RB("Command/Resume script", &resume_script);
1528 c->odbs->RB("Command/Debug script", &debug_script);
1529 c->odbs->RB("Command/Step over", &step_over);
1530 c->odbs->RB("Command/Load new file", &load_new_file);
1531 db_get_value(c->hDB, c->hSeq, "Command/Goto line", &goto_line, &goto_line_size, TID_INT, TRUE);
1532
1533 if (load_new_file) {
1534 std::string filename;
1535 c->odbs->RS("State/Filename", &filename);
1536
1537 // printf("## load new file %s\n", filename.c_str());
1538
1539 if (filename.find("..") != std::string::npos) {
1540 mstrlcpy(seqp->error, "Cannot load \"", sizeof(seqp->error));
1541 mstrlcat(seqp->error, filename.c_str(), sizeof(seqp->error));
1542 mstrlcat(seqp->error, "\": file names with \"..\" is not permitted", sizeof(seqp->error));
1543 seq_write(*seqp, c);
1544 } else if (filename.find(".msl") == std::string::npos) {
1545 mstrlcpy(seqp->error, "Cannot load \"", sizeof(seqp->error));
1546 mstrlcat(seqp->error, filename.c_str(), sizeof(seqp->error));
1547 mstrlcat(seqp->error, "\": file name should end with \".msl\"", sizeof(seqp->error));
1548 seq_write(*seqp, c);
1549 } else {
1550 mstrlcpy(seqp->filename, filename.c_str(), sizeof(seqp->filename));
1551 std::string path = cm_expand_env(seqp->path);
1552 if (path.length() > 0 && path.back() != '/') {
1553 path += "/";
1554 }
1555 HNDLE hDB;
1557 seq_clear(*seqp);
1558 seq_open_file(filename.c_str(), *seqp, c);
1559 seq_write(*seqp, c);
1560 }
1561
1562 // indicate that we successfully load the new file
1563 c->odbs->WB("Command/Load new file", false);
1564
1565 // printf("## Set 'load new file' false\n");
1566 }
1567
1568 if (goto_line != 0) {
1569 int zero = 0;
1570
1571 // Reset the command immediately so a failed goto does not repeat forever.
1572 db_set_value(c->hDB, c->hSeq, "Command/Goto line", &zero, sizeof(zero), 1, TID_INT);
1573 seq_goto_line(*seqp, c, goto_line);
1574 }
1575
1576 if (start_script) {
1577 c->odbs->WB("Command/Start script", false);
1578
1579 bool seq_running = false;
1580 c->odbs->RB("State/running", &seq_running);
1581
1582 if (!seq_running) {
1583 seq_start(*seqp, c, false);
1584 } else {
1585 cm_msg(MTALK, "sequencer", "Sequencer is already running");
1586 }
1587 }
1588
1589 if (stop_immediately && c->pnseq != NULL) {
1590 if (!goto_at_exit(*seqp, c)) {
1591 c->odbs->WB("Command/Stop immediately", false);
1592 seq_stop(*seqp, c);
1593 cm_msg(MTALK, "sequencer", "Sequencer is finished by \"stop immediately\".");
1594 } else {
1595 c->odbs->WB("Command/Stop immediately", false);
1596 cm_msg(MTALK, "sequencer", "Sequencer received \"stop immediately\". Executing ATEXIT.");
1597 }
1598 }
1599
1600 if (stop_after_run) {
1601 c->odbs->WB("Command/Stop after run", false);
1602 seq_stop_after_run(*seqp, c);
1603 }
1604
1605 if (cancel_stop_after_run) {
1606 c->odbs->WB("Command/Cancel stop after run", false);
1608 }
1609
1610 if (pause_script) {
1611 seq_pause(*seqp, c, true);
1612 c->odbs->WB("Command/Pause script", false);
1613 cm_msg(MTALK, "sequencer", "Sequencer is paused.");
1614 }
1615
1616 if (resume_script) {
1617 seq_pause(*seqp, c, false);
1618 c->odbs->WB("Command/Resume script", false);
1619 cm_msg(MTALK, "sequencer", "Sequencer is resumed.");
1620 }
1621
1622 if (debug_script) {
1623 c->odbs->WB("Command/Debug script", false);
1624
1625 bool seq_running = false;
1626 c->odbs->RB("State/running", &seq_running);
1627
1628 if (!seq_running) {
1629 seq_start(*seqp, c, true);
1630 } else {
1631 cm_msg(MTALK, "sequencer", "Sequencer is already running");
1632 }
1633 }
1634
1635 if (step_over) {
1636 seq_step_over(*seqp, c);
1637 c->odbs->WB("Command/Step over", false);
1638 }
1639}
std::string cm_expand_env(const char *str)
Definition midas.cxx:7721
#define TID_INT
Definition midas.h:338
INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT *buf_size, DWORD type, BOOL create)
Definition odb.cxx:5185
INT db_set_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type)
Definition odb.cxx:5028
static void seq_stop_after_run(SEQUENCER &seq, SeqCon *c)
static BOOL seq_goto_line(SEQUENCER &seq, SeqCon *c, int target_sline)
static void seq_stop(SEQUENCER &seq, SeqCon *c)
static BOOL goto_at_exit(SEQUENCER &seq, SeqCon *c)
static void seq_step_over(SEQUENCER &seq, SeqCon *c)
static void seq_cancel_stop_after_run(SEQUENCER &seq, SeqCon *c)
static void seq_pause(SEQUENCER &seq, SeqCon *c, bool flag)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ seq_write()

void seq_write ( const SEQUENCER seq,
SeqCon c 
)

Definition at line 1084 of file msequencer.cxx.

1084 {
1085 int status;
1086 HNDLE hKey;
1087
1088 status = db_find_key(c->hDB, c->hSeq, "State", &hKey);
1089 if (status != DB_SUCCESS) {
1090 cm_msg(MERROR, "seq_write", "Cannot find /Sequencer/State in ODB, db_find_key() status %d", status);
1091 return;
1092 }
1093 status = db_set_record(c->hDB, hKey, (void *) &seq, sizeof(SEQUENCER), 0);
1094 if (status != DB_SUCCESS) {
1095 cm_msg(MERROR, "seq_write", "Cannot write to ODB /Sequencer/State, db_set_record() status %d", status);
1096 return;
1097 }
1098}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sequencer()

void sequencer ( SEQUENCER seq,
SeqCon c 
)

Definition at line 1713 of file msequencer.cxx.

1713 {
1714 PMXML_NODE pn, pr, pt, pe;
1715 char odbpath[256], data[256], str[1024], name[32], op[32];
1716 std::string value;
1717 char list[100][XNAME_LENGTH];
1718 int i, j, l, n, status, size, index1, index2, state, run_number, cont;
1719 HNDLE hKey, hKeySeq;
1720 KEY key;
1721 double d;
1722 BOOL skip_step = FALSE;
1723
1724 if (!seq.running || seq.paused) {
1725 ss_sleep(10);
1726 return;
1727 }
1728
1729 if (c->pnseq == NULL) {
1730 seq_stop(seq, c);
1731 mstrlcpy(seq.error, "No script loaded", sizeof(seq.error));
1732 seq_write(seq, c);
1733 ss_sleep(10);
1734 return;
1735 }
1736
1737 db_find_key(c->hDB, c->hSeq, "State", &hKeySeq);
1738 if (!hKeySeq)
1739 return;
1740
1741 // Retrieve last midas message and put it into seq structure (later sent to ODB via db_set_record()
1742 cm_msg_retrieve(1, str, sizeof(str));
1743 str[19] = 0;
1744 strcpy(seq.last_msg, str+11);
1745
1746 pr = mxml_find_node(c->pnseq, "RunSequence");
1747 if (!pr) {
1748 seq_error(seq, c, "Cannot find &lt;RunSequence&gt; tag in XML file");
1749 return;
1750 }
1751
1752 int last_line = mxml_get_line_number_end(pr);
1753
1754 /* check for Subroutine end */
1755 if (seq.stack_index > 0 && seq.current_line_number == seq.subroutine_end_line[seq.stack_index - 1]) {
1756 seq_read(&seq, c);
1757 seq.subroutine_end_line[seq.stack_index - 1] = 0;
1758
1759 if (seq.subroutine_return_line[seq.stack_index - 1] == -1) {
1760 // end of atexit subroutine
1761 seq_stop(seq, c);
1762 cm_msg(MTALK, "sequencer", "Sequencer is finished.");
1763
1764 seq_start_next(seq, c);
1765 return;
1766 }
1767
1769 seq.subroutine_return_line[seq.stack_index - 1] = 0;
1770 seq.subroutine_call_line[seq.stack_index - 1] = 0;
1771 seq.ssubroutine_call_line[seq.stack_index - 1] = 0;
1772 seq.stack_index--;
1773 seq_write(seq, c);
1774 return;
1775 }
1776
1777 /* check for last line of script */
1778 if (seq.current_line_number > last_line) {
1779
1780 if (!goto_at_exit(seq, c)) {
1781 seq_stop(seq, c);
1782 cm_msg(MTALK, "sequencer", "Sequencer is finished.");
1783
1784 seq_start_next(seq, c);
1785 }
1786 return;
1787 }
1788
1789 /* check for loop end */
1790 for (i = SEQ_NEST_LEVEL_LOOP-1; i >= 0; i--)
1791 if (seq.loop_start_line[i] > 0)
1792 break;
1793 if (i >= 0) {
1794 if (seq.current_line_number == seq.loop_end_line[i]) {
1795 size = sizeof(seq);
1796 db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
1797
1798 if (seq.loop_counter[i] == seq.loop_n[i]) {
1799 seq.loop_counter[i] = 0;
1800 seq.loop_start_line[i] = 0;
1801 seq.sloop_start_line[i] = 0;
1802 seq.loop_end_line[i] = 0;
1803 seq.sloop_end_line[i] = 0;
1804 seq.loop_n[i] = 0;
1805 seq.current_line_number++;
1806 } else {
1807 pn = mxml_get_node_at_line(c->pnseq, seq.loop_start_line[i]);
1808 if (mxml_get_attribute(pn, "var")) {
1809 mstrlcpy(name, mxml_get_attribute(pn, "var"), sizeof(name));
1810 if (mxml_get_attribute(pn, "values")) {
1811 mstrlcpy(data, mxml_get_attribute(pn, "values"), sizeof(data));
1812 strbreak(data, list, 100, ",", FALSE);
1813 value = eval_var(seq, c, list[seq.loop_counter[i]]);
1814 } else if (mxml_get_attribute(pn, "n")) {
1815 value = std::to_string(seq.loop_counter[i] + 1);
1816 }
1817 sprintf(str, "Variables/%s", name); // FIXME: unsafe
1818 size = value.length() + 1;
1819 if (size < 32)
1820 size = 32;
1821 db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
1822 }
1823 seq.loop_counter[i]++;
1824 seq.current_line_number = seq.loop_start_line[i] + 1;
1825 }
1826 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
1827 return;
1828 }
1829 }
1830
1831 /* check for end of "if" statement */
1832 if (seq.if_index > 0 && seq.current_line_number == seq.if_endif_line[seq.if_index - 1]) {
1833 size = sizeof(seq);
1834 db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
1835 seq.if_index--;
1836 seq.if_line[seq.if_index] = 0;
1837 seq.if_else_line[seq.if_index] = 0;
1838 seq.if_endif_line[seq.if_index] = 0;
1839 seq.current_line_number++;
1840 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
1841 return;
1842 }
1843
1844 /* check for ODBSubdir end */
1845 if (seq.current_line_number == seq.subdir_end_line) {
1846 size = sizeof(seq);
1847 db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
1848 seq.subdir_end_line = 0;
1849 seq.subdir[0] = 0;
1851 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
1852 return;
1853 }
1854
1855 /* find node belonging to current line */
1856 pn = mxml_get_node_at_line(c->pnseq, seq.current_line_number);
1857 if (!pn) {
1858 size = sizeof(seq);
1859 db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
1860 seq.current_line_number++;
1861 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
1862 return;
1863 }
1864
1865 /* set MSL line from current element and put script into ODB if changed (library call) */
1866 pn = mxml_get_node_at_line(c->pnseq, seq.current_line_number);
1867 if (pn) {
1868 if (seq.follow_libraries) {
1869 if (mxml_get_attribute(pn, "l"))
1870 seq.scurrent_line_number = atoi(mxml_get_attribute(pn, "l"));
1871 if (mxml_get_attribute(pn, "fn")) {
1872 std::string filename = mxml_get_attribute(pn, "fn");
1873
1874 // load file into ODB if changed
1875 if (filename != std::string(seq.sfilename)) {
1876 if (loadMSL(c->odbs, filename))
1877 mstrlcpy(seq.sfilename, filename.c_str(), sizeof(seq.sfilename));
1878 }
1879 }
1880 } else {
1881 if (mxml_get_attribute(pn, "l") && (!mxml_get_attribute(pn, "lvl") || atoi(mxml_get_attribute(pn, "lvl")) == 0))
1882 seq.scurrent_line_number = atoi(mxml_get_attribute(pn, "l"));
1883 }
1884 }
1885
1886
1887 // out-comment following lines for debug output
1888#if 0
1889 if (seq.scurrent_line_number >= 0) {
1890 midas::odb o("/" + c->odb_path + "/Script/Lines");
1891 std::string s = o[seq.scurrent_line_number - 1];
1892 printf("%3d: %s\n", seq.scurrent_line_number, s.c_str());
1893 }
1894#endif
1895
1896 if (equal_ustring(mxml_get_name(pn), "PI") || equal_ustring(mxml_get_name(pn), "RunSequence") ||
1897 equal_ustring(mxml_get_name(pn), "Comment")) {
1898 // just skip
1899 seq.current_line_number++;
1900 skip_step = TRUE;
1901 }
1902
1903 /*---- ODBSubdir ----*/
1904 else if (equal_ustring(mxml_get_name(pn), "ODBSubdir")) {
1905 if (!mxml_get_attribute(pn, "path")) {
1906 seq_error(seq, c, "Missing attribute \"path\"");
1907 } else {
1908
1909 std::string s = mxml_get_attribute(pn, "path");
1910 if (s.find('$') != std::string::npos)
1911 s = eval_var(seq, c, s);
1912
1913 mstrlcpy(seq.subdir, s.c_str(), sizeof(seq.subdir));
1914 if (mxml_get_attribute(pn, "notify"))
1915 seq.subdir_not_notify = !atoi(mxml_get_attribute(pn, "notify"));
1916 seq.subdir_end_line = mxml_get_line_number_end(pn);
1917 seq.current_line_number++;
1918 }
1919 }
1920
1921 /*---- ODBSet ----*/
1922 else if (equal_ustring(mxml_get_name(pn), "ODBSet")) {
1923 if (!mxml_get_attribute(pn, "path")) {
1924 seq_error(seq, c, "Missing attribute \"path\"");
1925 } else {
1926 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
1927 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
1928 mstrlcat(odbpath, "/", sizeof(odbpath));
1929 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
1930
1931 if (strchr(odbpath, '$')) {
1932 if (strchr(odbpath, '[')) {
1933 // keep $ in index for later evaluation
1934 std::string s(odbpath);
1935 std::string s1 = s.substr(0, s.find('['));
1936 std::string s2 = s.substr(s.find('['));
1937 s1 = eval_var(seq, c, s1);
1938 s1 += s2;
1939 mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
1940 } else {
1941 // evaluate variable in path
1942 std::string s(odbpath);
1943 s = eval_var(seq, c, s);
1944 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
1945 }
1946 }
1947
1948 int notify = TRUE;
1949 if (seq.subdir_not_notify)
1950 notify = FALSE;
1951 if (mxml_get_attribute(pn, "notify"))
1952 notify = atoi(mxml_get_attribute(pn, "notify"));
1953
1954 index1 = index2 = 0;
1955 seq_array_index(seq, c, odbpath, &index1, &index2);
1956
1957 if (index1 < -1 || index2 < 0) {
1958 seq_error(seq, c, "Negative index not allowed");
1959 return;
1960 }
1961
1962 if (index1 > 1E6 || index2 > 1E6) {
1963 seq_error(seq, c, "Index too large for ODB");
1964 return;
1965 }
1966
1967 value = eval_var(seq, c, mxml_get_value(pn));
1968
1969 status = set_all_matching(c->hDB, 0, odbpath, (char *)value.c_str(), index1, index2, notify);
1970
1971 if (status == DB_SUCCESS) {
1972 size = sizeof(seq);
1973 db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
1974 seq.current_line_number++;
1975 } else if (status == DB_NO_KEY) {
1976 sprintf(str, "ODB key \"%s\" not found", odbpath); // FIXME: unsafe
1977 seq_error(seq, c, str);
1978 } else if (status == DB_INVALID_PARAM) {
1979 sprintf(str, "Invalid index %d for ODB key \"%s\"", index1, odbpath); // FIXME: unsafe
1980 seq_error(seq, c, str);
1981 } else {
1982 //something went really wrong
1983 sprintf(str, "Internal error %d", status);
1984 seq_error(seq, c, str);
1985 return;
1986 }
1987 }
1988 }
1989
1990 /*---- ODBLoad ----*/
1991 else if (equal_ustring(mxml_get_name(pn), "ODBLoad")) {
1992 if (mxml_get_value(pn)[0] == '/') {
1993
1994 // path relative to the one set in <exp>/userfiles/sequencer
1995 std::string path(cm_get_path());
1996 path += "userfiles/sequencer/";
1997 value = path;
1998 value += std::string(mxml_get_value(pn)+1);
1999 } else {
2000 // relative path to msl file
2001 std::string path(cm_get_path());
2002 path += "userfiles/sequencer/";
2003 path += seq.path;
2004 value = path;
2005 if (value.back() != '/')
2006 value += "/";
2007 value += mxml_get_value(pn);
2008 }
2009
2010 if (value.find('$') != std::string::npos)
2011 value = eval_var(seq, c, value);
2012
2013 // if path attribute is given
2014 if (mxml_get_attribute(pn, "path")) {
2015 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
2016 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
2017 mstrlcat(odbpath, "/", sizeof(odbpath));
2018 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
2019
2020 if (strchr(odbpath, '$')) {
2021 std::string s(odbpath);
2022 s = eval_var(seq, c, s);
2023 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2024 }
2025
2026 // load at that key, if exists
2027 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2028 if (status != DB_SUCCESS) {
2029 seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
2030 return;
2031 } else {
2032 status = db_load(c->hDB, hKey, value.c_str(), FALSE);
2033 }
2034 } else {
2035 // otherwise load at root
2036 status = db_load(c->hDB, 0, value.c_str(), FALSE);
2037 }
2038
2039 if (status == DB_SUCCESS) {
2040 size = sizeof(seq);
2041 db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
2042 seq.current_line_number++;
2043 } else if (status == DB_FILE_ERROR) {
2044 seq_error(seq, c, msprintf("Error reading file \"%s\"", value.c_str()).c_str());
2045 } else {
2046 //something went really wrong
2047 seq_error(seq, c, "Internal error loading ODB file!");
2048 return;
2049 }
2050 }
2051
2052 /*---- ODBSave ----*/
2053 else if (equal_ustring(mxml_get_name(pn), "ODBSave")) {
2054 if (mxml_get_value(pn)[0] == '/') {
2055
2056 // path relative to the one set in <exp>/userfiles/sequencer
2057 std::string path(cm_get_path());
2058 path += "userfiles/sequencer/";
2059 value = path;
2060 value += std::string(mxml_get_value(pn)+1);
2061 } else {
2062 // relative path to msl file
2063 std::string path(cm_get_path());
2064 path += "userfiles/sequencer/";
2065 path += seq.path;
2066 value = path;
2067 value += seq.filename;
2068 size_t pos = value.find_last_of('/');
2069 if (pos != std::string::npos)
2070 value = value.substr(0, pos);
2071 value += mxml_get_value(pn);
2072 }
2073
2074 if (value.find('$') != std::string::npos)
2075 value = eval_var(seq, c, value);
2076
2077 // if path attribute is given
2078 if (mxml_get_attribute(pn, "path") && *mxml_get_attribute(pn, "path")) {
2079 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
2080 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
2081 mstrlcat(odbpath, "/", sizeof(odbpath));
2082 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
2083
2084 if (strchr(odbpath, '$')) {
2085 std::string s(odbpath);
2086 s = eval_var(seq, c, s);
2087 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2088 }
2089
2090 // find key or subdirectory to save
2091 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2092 if (status != DB_SUCCESS) {
2093 seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
2094 return;
2095 } else {
2096 if (strstr(value.c_str(), ".json") || strstr(value.c_str(), ".JSON") || strstr(value.c_str(), ".js") || strstr(value.c_str(), ".JS"))
2098 else if (strstr(value.c_str(), ".xml") || strstr(value.c_str(), ".XML"))
2099 status = db_save_xml(c->hDB, hKey, value.c_str());
2100 else
2101 status = db_save(c->hDB, hKey, value.c_str(), FALSE);
2102 if (status != DB_SUCCESS) {
2103 seq_error(seq, c, msprintf("Cannot save file \"%s\", error %d", value.c_str(), status).c_str());
2104 return;
2105 }
2106 }
2107 } else {
2108 seq_error(seq, c, "No ODB path specified in ODBSAVE command");
2109 return;
2110 }
2111
2112 if (status == DB_SUCCESS) {
2113 size = sizeof(seq);
2114 db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
2115 seq.current_line_number++;
2116 } else if (status == DB_FILE_ERROR) {
2117 seq_error(seq, c, msprintf("Error reading file \"%s\"", value.c_str()).c_str());
2118 } else {
2119 //something went really wrong
2120 seq_error(seq, c, "Internal error loading ODB file!");
2121 return;
2122 }
2123 }
2124
2125 /*---- ODBGet ----*/
2126 else if (equal_ustring(mxml_get_name(pn), "ODBGet")) {
2127 if (!mxml_get_attribute(pn, "path")) {
2128 seq_error(seq, c, "Missing attribute \"path\"");
2129 } else {
2130 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
2131 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
2132 mstrlcat(odbpath, "/", sizeof(odbpath));
2133 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
2134
2135 if (strchr(odbpath, '$')) {
2136 if (strchr(odbpath, '[')) {
2137 // keep $ in index for later evaluation
2138 std::string s(odbpath);
2139 std::string s1 = s.substr(0, s.find('['));
2140 std::string s2 = s.substr(s.find('['));
2141 s1 = eval_var(seq, c, s1);
2142 s1 += s2;
2143 mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
2144 } else {
2145 // evaluate variable in path
2146 std::string s(odbpath);
2147 s = eval_var(seq, c, s);
2148 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2149 }
2150 }
2151
2152 /* check if index is supplied */
2153 index1 = index2 = 0;
2154 seq_array_index(seq, c, odbpath, &index1, &index2);
2155
2156 mstrlcpy(name, mxml_get_value(pn), sizeof(name));
2157 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2158 if (status != DB_SUCCESS) {
2159 seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
2160 return;
2161 } else {
2162 db_get_key(c->hDB, hKey, &key);
2163 size = sizeof(data);
2164
2165 status = db_get_data_index(c->hDB, hKey, data, &size, index1, key.type);
2166 if (key.type == TID_BOOL)
2167 value = *((int *) data) > 0 ? "1" : "0";
2168 else
2169 value = db_sprintf(data, size, 0, key.type);
2170
2171 sprintf(str, "Variables/%s", name); // FIXME: unsafe
2172 size = value.length() + 1;
2173 if (size < 32)
2174 size = 32;
2175 db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
2176
2177 size = sizeof(seq);
2178 db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
2179 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2180 }
2181 }
2182 }
2183
2184 /*---- ODBLookup ----*/
2185 else if (equal_ustring(mxml_get_name(pn), "ODBLookup")) {
2186 if (!mxml_get_attribute(pn, "path")) {
2187 seq_error(seq, c, "Missing attribute \"path\"");
2188 } else if (!mxml_get_attribute(pn, "string")) {
2189 seq_error(seq, c, "Missing attribute \"string\"");
2190 } else {
2191 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
2192 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
2193 mstrlcat(odbpath, "/", sizeof(odbpath));
2194 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
2195
2196 if (strchr(odbpath, '$')) {
2197 if (strchr(odbpath, '[')) {
2198 // keep $ in index for later evaluation
2199 std::string s(odbpath);
2200 std::string s1 = s.substr(0, s.find('['));
2201 std::string s2 = s.substr(s.find('['));
2202 s1 = eval_var(seq, c, s1);
2203 s1 += s2;
2204 mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
2205 } else {
2206 // evaluate variable in path
2207 std::string s(odbpath);
2208 s = eval_var(seq, c, s);
2209 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2210 }
2211 }
2212
2213 mstrlcpy(name, mxml_get_value(pn), sizeof(name));
2214
2215 auto s = eval_var(seq, c, mxml_get_attribute(pn, "string"));
2216 mstrlcpy(str, s.c_str(), sizeof(str));
2217
2218 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2219 if (status != DB_SUCCESS) {
2220 seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
2221 return;
2222 } else {
2223 db_get_key(c->hDB, hKey, &key);
2224
2225 // search for "str"
2226 for (i = 0 ; i<key.num_values ; i++) {
2227 size = sizeof(data);
2228 status = db_get_data_index(c->hDB, hKey, data, &size, i, key.type);
2229 if (strcmp((const char *)data, str) == 0)
2230 break;
2231 }
2232
2233 size = sizeof(i);
2234 if (i < key.num_values)
2235 value = db_sprintf(&i, size, 0, TID_INT);
2236 else {
2237 snprintf(str, sizeof(str), "\"ODBLOOKUP %s\" did not find string \"%s\"", odbpath, s.c_str());
2238 seq_error(seq, c, str);
2239 return;
2240 }
2241
2242 snprintf(str, sizeof(str), "Variables/%s", name);
2243 size = value.length() + 1;
2244 if (size < 32)
2245 size = 32;
2246 db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
2247
2248 size = sizeof(seq);
2249 db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
2250 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2251 }
2252 }
2253 }
2254
2255 /*---- ODBInc ----*/
2256 else if (equal_ustring(mxml_get_name(pn), "ODBInc")) {
2257 if (!mxml_get_attribute(pn, "path")) {
2258 seq_error(seq, c, "Missing attribute \"path\"");
2259 } else {
2260 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
2261 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
2262 mstrlcat(odbpath, "/", sizeof(odbpath));
2263 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
2264
2265 if (strchr(odbpath, '$')) {
2266 if (strchr(odbpath, '[')) {
2267 // keep $ in index for later evaluation
2268 std::string s(odbpath);
2269 std::string s1 = s.substr(0, s.find('['));
2270 std::string s2 = s.substr(s.find('['));
2271 s1 = eval_var(seq, c, s1);
2272 s1 += s2;
2273 mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
2274 } else {
2275 // evaluate variable in path
2276 std::string s(odbpath);
2277 s = eval_var(seq, c, s);
2278 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2279 }
2280 }
2281
2282 index1 = index2 = 0;
2283 seq_array_index(seq, c, odbpath, &index1, &index2);
2284
2285 value = eval_var(seq, c, mxml_get_value(pn));
2286
2287 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2288 if (status != DB_SUCCESS) {
2289 seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
2290 } else {
2291 db_get_key(c->hDB, hKey, &key);
2292 size = sizeof(data);
2293 db_get_data_index(c->hDB, hKey, data, &size, index1, key.type);
2294 std::string s = db_sprintf(data, size, 0, key.type);
2295 d = std::stof(s);
2296 d += std::stof(value);
2297 sprintf(str, "%lg", d);
2298 size = sizeof(data);
2299 db_sscanf(str, data, &size, 0, key.type);
2300
2301 int notify = TRUE;
2302 if (seq.subdir_not_notify)
2303 notify = FALSE;
2304 if (mxml_get_attribute(pn, "notify"))
2305 notify = atoi(mxml_get_attribute(pn, "notify"));
2306
2307 db_set_data_index1(c->hDB, hKey, data, key.item_size, index1, key.type, notify);
2308 seq.current_line_number++;
2309 }
2310 }
2311 }
2312
2313 /*---- ODBDelete ----*/
2314 else if (equal_ustring(mxml_get_name(pn), "ODBDelete")) {
2315 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
2316 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
2317 mstrlcat(odbpath, "/", sizeof(odbpath));
2318 mstrlcat(odbpath, mxml_get_value(pn), sizeof(odbpath));
2319
2320 if (strchr(odbpath, '$')) {
2321 std::string s(odbpath);
2322 s = eval_var(seq, c, s);
2323 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2324 }
2325
2326 status = db_delete(c->hDB, 0, odbpath);
2327 if (status != DB_SUCCESS) {
2328 seq_error(seq, c, msprintf("Cannot delete ODB key \"%s\"", odbpath).c_str());
2329 } else {
2330 seq.current_line_number++;
2331 }
2332 }
2333
2334 /*---- ODBCreate ----*/
2335 else if (equal_ustring(mxml_get_name(pn), "ODBCreate")) {
2336 if (!mxml_get_attribute(pn, "path")) {
2337 seq_error(seq, c, "Missing attribute \"path\"");
2338 } else if (!mxml_get_attribute(pn, "type")) {
2339 seq_error(seq, c, "Missing attribute \"type\"");
2340 } else {
2341 mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
2342 if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
2343 mstrlcat(odbpath, "/", sizeof(odbpath));
2344 mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
2345
2346 if (strchr(odbpath, '$')) {
2347 std::string s(odbpath);
2348 s = eval_var(seq, c, s);
2349 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2350 }
2351
2352 /* get TID */
2353 unsigned int tid;
2354 for (tid = 0; tid < TID_LAST; tid++) {
2355 if (equal_ustring(rpc_tid_name(tid), mxml_get_attribute(pn, "type")))
2356 break;
2357 }
2358
2359 if (tid == TID_LAST)
2360 seq_error(seq, c, "Type must be one of UINT8,INT8,UINT16,INT16,UINT32,INT32,BOOL,FLOAT,DOUBLE,STRING");
2361 else {
2362
2363 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2364 if (status == DB_SUCCESS) {
2365 db_get_key(c->hDB, hKey, &key);
2366 if (key.type != tid) {
2367 db_delete_key(c->hDB, hKey);
2368 status = db_create_key(c->hDB, 0, odbpath, tid);
2369 }
2370 } else {
2371 status = db_create_key(c->hDB, 0, odbpath, tid);
2372 char dummy[32];
2373 memset(dummy, 0, sizeof(dummy));
2374 if (tid == TID_STRING || tid == TID_LINK)
2375 db_set_value(c->hDB, 0, odbpath, dummy, 32, 1, tid);
2376 }
2377
2378 if (status != DB_SUCCESS && status != DB_CREATED) {
2379 seq_error(seq, c, msprintf("Cannot create ODB key \"%s\", error code %d", odbpath, status).c_str());
2380 } else {
2381 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2382 if (mxml_get_attribute(pn, "size")) {
2383 i = atoi(eval_var(seq, c, mxml_get_attribute(pn, "size")).c_str());
2384 if (i > 1)
2385 db_set_num_values(c->hDB, hKey, i);
2386 }
2387 seq.current_line_number++;
2388 }
2389 }
2390 }
2391 }
2392
2393 /*---- RunDescription ----*/
2394 else if (equal_ustring(mxml_get_name(pn), "RunDescription")) {
2395 db_set_value(c->hDB, 0, "/Experiment/Run Parameters/Run Description", mxml_get_value(pn), 256, 1, TID_STRING);
2396 seq.current_line_number++;
2397 }
2398
2399 /*---- Script ----*/
2400 else if (equal_ustring(mxml_get_name(pn), "Script")) {
2401 sprintf(str, "%s", mxml_get_value(pn)); // FIXME: unsafe
2402
2403 if (mxml_get_attribute(pn, "params")) {
2404 mstrlcpy(data, mxml_get_attribute(pn, "params"), sizeof(data));
2405 n = strbreak(data, list, 100, ",", FALSE);
2406 for (i = 0; i < n; i++) {
2407
2408 value = eval_var(seq, c, list[i]);
2409
2410 mstrlcat(str, " ", sizeof(str));
2411 mstrlcat(str, value.c_str(), sizeof(str));
2412 }
2413 }
2414
2415 std::string s(str);
2417 std::string r = ss_execs(s.c_str());
2418
2419 // put result into SCRIPT_RESULT variable
2420 db_set_value(c->hDB, c->hSeq, "Variables/SCRIPT_RESULT", r.c_str(), r.length(), 1, TID_STRING);
2421
2422 seq.current_line_number++;
2423 }
2424
2425 /*---- Transition ----*/
2426 else if (equal_ustring(mxml_get_name(pn), "Transition")) {
2427 if (equal_ustring(mxml_get_value(pn), "Start")) {
2428 if (!seq.transition_request) {
2430 size = sizeof(state);
2431 db_get_value(c->hDB, 0, "/Runinfo/State", &state, &size, TID_INT32, FALSE);
2432 if (state != STATE_RUNNING) {
2433 size = sizeof(run_number);
2434 db_get_value(c->hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT32, FALSE);
2436 if (status != CM_SUCCESS) {
2437 seq_error(seq, c, msprintf("Cannot start run: %s", str).c_str());
2438 }
2439 }
2440 } else {
2441 // Wait until transition has finished
2442 size = sizeof(state);
2443 db_get_value(c->hDB, 0, "/Runinfo/State", &state, &size, TID_INT32, FALSE);
2444 if (state == STATE_RUNNING) {
2446 seq.current_line_number++;
2447 }
2448 }
2449 } else if (equal_ustring(mxml_get_value(pn), "Stop")) {
2450 if (!seq.transition_request) {
2452 size = sizeof(state);
2453 db_get_value(c->hDB, 0, "/Runinfo/State", &state, &size, TID_INT32, FALSE);
2454 if (state != STATE_STOPPED) {
2457 // do nothing
2458 } else if (status != CM_SUCCESS) {
2459 seq_error(seq, c, msprintf("Cannot stop run: %s", str).c_str());
2460 }
2461 }
2462 } else {
2463 // Wait until transition has finished
2464 size = sizeof(state);
2465 db_get_value(c->hDB, 0, "/Runinfo/State", &state, &size, TID_INT32, FALSE);
2466 if (state == STATE_STOPPED) {
2467 size = sizeof(seq);
2468 db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
2469
2471
2472 if (seq.stop_after_run) {
2473 seq.stop_after_run = FALSE;
2474
2475 if (!goto_at_exit(seq, c)) {
2476 seq.running = FALSE;
2477 seq.finished = TRUE;
2478 seq_stop(seq, c);
2479 cm_msg(MTALK, "sequencer", "Sequencer is finished by \"stop after current run\".");
2480 } else {
2481 cm_msg(MTALK, "sequencer", "Sequencer is going to finish by \"stop after current run\". Executing ATEXIT.");
2482 }
2483 } else {
2484 seq.current_line_number++;
2485 }
2486
2487 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
2488 }
2489 }
2490 } else {
2491 seq_error(seq, c, msprintf("Invalid transition \"%s\"", mxml_get_value(pn)).c_str());
2492 return;
2493 }
2494 }
2495
2496 /*---- Wait ----*/
2497 else if (equal_ustring(mxml_get_name(pn), "Wait")) {
2498 if (equal_ustring(mxml_get_attribute(pn, "for"), "Events")) {
2499 n = atoi(eval_var(seq, c, mxml_get_value(pn)).c_str());
2500 seq.wait_limit = (float) n;
2501 strcpy(seq.wait_type, "Events");
2502 seq.timeout_limit = 0;
2503 seq.timeout_value = 0;
2504 size = sizeof(d);
2505 db_get_value(c->hDB, 0, "/Equipment/Trigger/Statistics/Events sent", &d, &size, TID_DOUBLE, FALSE);
2506 seq.wait_value = (float) d;
2507 if (d >= n) {
2508 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2509 seq.wait_limit = 0;
2510 seq.wait_value = 0;
2511 seq.timeout_limit = 0;
2512 seq.timeout_value = 0;
2513 seq.wait_type[0] = 0;
2514 seq.wait_odb[0] = 0;
2515 }
2516 seq.wait_value = (float) d;
2517 } else if (equal_ustring(mxml_get_attribute(pn, "for"), "ODBValue")) {
2518 seq.wait_limit = (float) atof(eval_var(seq, c, mxml_get_value(pn)).c_str());
2519 mstrlcpy(seq.wait_type, "ODB", sizeof(seq.wait_type));
2520 if (!mxml_get_attribute(pn, "path")) {
2521 seq_error(seq, c, "\"path\" must be given for ODB values");
2522 return;
2523 } else {
2524 mstrlcpy(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
2525 mstrlcpy(seq.wait_odb, odbpath, sizeof(seq.wait_odb));
2526
2527 if (strchr(odbpath, '$')) {
2528 if (strchr(odbpath, '[')) {
2529 // keep $ in index for later evaluation
2530 std::string s(odbpath);
2531 std::string s1 = s.substr(0, s.find('['));
2532 std::string s2 = s.substr(s.find('['));
2533 s1 = eval_var(seq, c, s1);
2534 s1 += s2;
2535 mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
2536 } else {
2537 // evaluate variable in path
2538 std::string s(odbpath);
2539 s = eval_var(seq, c, s);
2540 mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
2541 }
2542 }
2543
2544 index1 = index2 = 0;
2545 seq_array_index(seq, c, odbpath, &index1, &index2);
2546 status = db_find_key(c->hDB, 0, odbpath, &hKey);
2547 if (status != DB_SUCCESS) {
2548 seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
2549 return;
2550 } else {
2551 if (mxml_get_attribute(pn, "op"))
2552 mstrlcpy(op, mxml_get_attribute(pn, "op"), sizeof(op));
2553 else
2554 strcpy(op, "!=");
2555 mstrlcat(seq.wait_type, op, sizeof(seq.wait_type));
2556
2557 db_get_key(c->hDB, hKey, &key);
2558 size = sizeof(data);
2559 db_get_data_index(c->hDB, hKey, data, &size, index1, key.type);
2560 if (key.type == TID_BOOL)
2561 value = *((int *) data) > 0 ? "1" : "0";
2562 else
2563 value = db_sprintf(data, size, 0, key.type);
2564 cont = FALSE;
2565 seq.wait_value = std::stof(value);
2566 if (equal_ustring(op, ">=")) {
2567 cont = (seq.wait_value >= seq.wait_limit);
2568 } else if (equal_ustring(op, ">")) {
2569 cont = (seq.wait_value > seq.wait_limit);
2570 } else if (equal_ustring(op, "<=")) {
2571 cont = (seq.wait_value <= seq.wait_limit);
2572 } else if (equal_ustring(op, "<")) {
2573 cont = (seq.wait_value < seq.wait_limit);
2574 } else if (equal_ustring(op, "==")) {
2575 cont = (seq.wait_value == seq.wait_limit);
2576 } else if (equal_ustring(op, "!=")) {
2577 cont = (seq.wait_value != seq.wait_limit);
2578 } else {
2579 seq_error(seq, c, msprintf("Invalid comaprison \"%s\"", op).c_str());
2580 return;
2581 }
2582
2583 // Keep the existing ODB wait display semantics:
2584 // wait_limit = requested ODB target value
2585 // wait_value = current ODB value
2586 // Publish the optional timeout separately so the web interface can
2587 // display the ODB condition and a normal progress bar at the same time.
2588 bool timed_out = false;
2589 const char *timeout_attr = mxml_get_attribute(pn, "timeout");
2590 if (timeout_attr && timeout_attr[0]) {
2591 float timeout_sec = (float) atof(eval_var(seq, c, timeout_attr).c_str());
2592 if (timeout_sec < 0) {
2593 seq_error(seq, c, msprintf("Invalid WAIT ODBValue timeout \"%s\"", timeout_attr).c_str());
2594 return;
2595 }
2596
2597 // Use the same monotonic millisecond clock base as WAIT Seconds.
2598 if (seq.start_time == 0)
2599 seq.start_time = ss_millitime();
2600
2601 DWORD elapsed_ms = ss_millitime() - seq.start_time;
2602
2603 seq.timeout_limit = timeout_sec;
2604 seq.timeout_value = (float) elapsed_ms / 1000.0f;
2605 if (seq.timeout_value > seq.timeout_limit)
2606 seq.timeout_value = seq.timeout_limit;
2607
2608 timed_out = elapsed_ms > (DWORD) (1000.0f * timeout_sec);
2609 } else {
2610 seq.timeout_limit = 0;
2611 seq.timeout_value = 0;
2612 }
2613
2614 if (cont || timed_out) {
2615 if (mxml_get_attribute(pn, "status")) {
2616 const char *status_value = (cont && !timed_out) ? "1" : "0";
2617
2618 mstrlcpy(name, mxml_get_attribute(pn, "status"), sizeof(name));
2619 snprintf(str, sizeof(str), "Variables/%s", name);
2620 size = strlen(status_value) + 1;
2621 if (size < 32)
2622 size = 32;
2623 db_set_value(c->hDB, c->hSeq, str, status_value, size, 1, TID_STRING);
2624 }
2625
2626 if (timed_out && !cont) {
2627 cm_msg(MINFO, "sequencer", "WAIT ODBValue timeout after %.1f seconds: %s %s %g not satisfied",
2628 seq.timeout_limit, odbpath, op, seq.wait_limit);
2629 }
2630 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2631 seq.start_time = 0;
2632 seq.wait_limit = 0;
2633 seq.wait_value = 0;
2634 seq.timeout_limit = 0;
2635 seq.timeout_value = 0;
2636 seq.wait_type[0] = 0;
2637 seq.wait_odb[0] = 0;
2638 }
2639 }
2640 }
2641 } else if (equal_ustring(mxml_get_attribute(pn, "for"), "Seconds")) {
2642 seq.wait_limit = 1000.0f * (float) atof(eval_var(seq, c, mxml_get_value(pn)).c_str());
2643 strcpy(seq.wait_type, "Seconds");
2644 seq.timeout_limit = 0;
2645 seq.timeout_value = 0;
2646 if (seq.start_time == 0) {
2647 seq.start_time = ss_millitime();
2648 seq.wait_value = 0;
2649 } else {
2650 seq.wait_value = (float) (ss_millitime() - seq.start_time);
2651 if (seq.wait_value > seq.wait_limit)
2652 seq.wait_value = seq.wait_limit;
2653 }
2654 if (ss_millitime() - seq.start_time > (DWORD) seq.wait_limit) {
2655 seq.current_line_number++;
2656 seq.start_time = 0;
2657 seq.wait_limit = 0;
2658 seq.wait_value = 0;
2659 seq.timeout_limit = 0;
2660 seq.timeout_value = 0;
2661 seq.wait_type[0] = 0;
2662 seq.wait_odb[0] = 0;
2663 }
2664 } else {
2665 seq_error(seq, c, msprintf("Invalid wait attribute \"%s\"", mxml_get_attribute(pn, "for")).c_str());
2666 }
2667
2668 // sleep to keep the CPU from consuming 100%
2669 ss_sleep(1);
2670 }
2671
2672 /*---- Loop start ----*/
2673 else if (equal_ustring(mxml_get_name(pn), "Loop")) {
2674 for (i = 0; i < SEQ_NEST_LEVEL_LOOP; i++)
2675 if (seq.loop_start_line[i] == 0)
2676 break;
2677 if (i == SEQ_NEST_LEVEL_LOOP) {
2678 seq_error(seq, c, "Maximum loop nesting exceeded");
2679 return;
2680 }
2682 seq.loop_end_line[i] = mxml_get_line_number_end(pn);
2683 if (mxml_get_attribute(pn, "l"))
2684 seq.sloop_start_line[i] = atoi(mxml_get_attribute(pn, "l"));
2685 if (mxml_get_attribute(pn, "le"))
2686 seq.sloop_end_line[i] = atoi(mxml_get_attribute(pn, "le"));
2687 seq.loop_counter[i] = 1;
2688
2689 if (mxml_get_attribute(pn, "n")) {
2690 if (equal_ustring(mxml_get_attribute(pn, "n"), "infinite"))
2691 seq.loop_n[i] = -1;
2692 else {
2693 seq.loop_n[i] = atoi(eval_var(seq, c, mxml_get_attribute(pn, "n")).c_str());
2694 }
2695 value = "1";
2696 } else if (mxml_get_attribute(pn, "values")) {
2697 mstrlcpy(data, mxml_get_attribute(pn, "values"), sizeof(data));
2698 seq.loop_n[i] = strbreak(data, list, 100, ",", FALSE);
2699 value = eval_var(seq, c, list[0]);
2700 } else {
2701 seq_error(seq, c, "Missing \"var\" or \"n\" attribute");
2702 return;
2703 }
2704
2705 if (mxml_get_attribute(pn, "var")) {
2706 mstrlcpy(name, mxml_get_attribute(pn, "var"), sizeof(name));
2707 sprintf(str, "Variables/%s", name); // FIXME: unsafe
2708 size = value.length() + 1;
2709 if (size < 32)
2710 size = 32;
2711 db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
2712 }
2713
2714 seq.current_line_number++;
2715 }
2716
2717 /*---- Break ----*/
2718 else if (equal_ustring(mxml_get_name(pn), "Break")) {
2719
2720 // finish current if statement
2721 while (seq.if_index > 0 &&
2722 seq.current_line_number > seq.if_line[seq.if_index - 1] &&
2723 seq.current_line_number < seq.if_endif_line[seq.if_index-1]) {
2724 size = sizeof(seq);
2725 db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
2726 seq.if_index--;
2727 seq.if_line[seq.if_index] = 0;
2728 seq.if_else_line[seq.if_index] = 0;
2729 seq.if_endif_line[seq.if_index] = 0;
2730 seq.current_line_number++;
2731 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
2732 }
2733
2734 // goto next loop end
2735 for (i = 0; i < SEQ_NEST_LEVEL_LOOP; i++)
2736 if (seq.loop_start_line[i] == 0)
2737 break;
2738 if (i == 0) {
2739 seq_error(seq, c, "\"Break\" outside any loop");
2740 return;
2741 }
2742
2743 // force end of loop in next check
2744 seq.current_line_number = seq.loop_end_line[i-1];
2745 seq.loop_counter[i-1] = seq.loop_n[i-1];
2746 }
2747
2748 /*---- If ----*/
2749 else if (equal_ustring(mxml_get_name(pn), "If")) {
2750
2751 if (seq.if_index == SEQ_NEST_LEVEL_IF) {
2752 seq_error(seq, c, "Maximum number of nested if..endif exceeded");
2753 return;
2754 }
2755
2756 // store "if" and "endif" lines
2757 seq.if_line[seq.if_index] = seq.current_line_number;
2758 seq.if_endif_line[seq.if_index] = mxml_get_line_number_end(pn);
2759
2760 // search for "else" line
2761 seq.if_else_line[seq.if_index] = 0;
2762 for (j = seq.current_line_number + 1; j < mxml_get_line_number_end(pn) + 1; j++) {
2763 pe = mxml_get_node_at_line(c->pnseq, j);
2764
2765 // skip nested if..endif
2766 if (pe && equal_ustring(mxml_get_name(pe), "If")) {
2767 j = mxml_get_line_number_end(pe);
2768 continue;
2769 }
2770
2771 // store "else" if found
2772 if (pe && equal_ustring(mxml_get_name(pe), "Else")) {
2773 seq.if_else_line[seq.if_index] = j;
2774 break;
2775 }
2776 }
2777
2778 mstrlcpy(str, mxml_get_attribute(pn, "condition"), sizeof(str));
2779 i = eval_condition(seq, c, str);
2780 if (i < 0) {
2781 seq_error(seq, c, "Invalid number in comparison");
2782 return;
2783 }
2784
2785 if (i == 1)
2786 seq.current_line_number++;
2787 else if (seq.if_else_line[seq.if_index])
2788 seq.current_line_number = seq.if_else_line[seq.if_index] + 1;
2789 else
2791
2792 seq.if_index++;
2793 }
2794
2795 /*---- Else ----*/
2796 else if (equal_ustring(mxml_get_name(pn), "Else")) {
2797 // goto next "Endif"
2798 if (seq.if_index == 0) {
2799 seq_error(seq, c, "Unexpected Else");
2800 return;
2801 }
2802 seq.current_line_number = seq.if_endif_line[seq.if_index - 1];
2803 }
2804
2805 /*---- Exit ----*/
2806 else if (equal_ustring(mxml_get_name(pn), "Exit")) {
2807 seq_stop(seq, c);
2808 cm_msg(MTALK, "sequencer", "Sequencer is finished.");
2809 return;
2810 }
2811
2812 /*---- Goto ----*/
2813 else if (equal_ustring(mxml_get_name(pn), "Goto")) {
2814 if (!mxml_get_attribute(pn, "line") && !mxml_get_attribute(pn, "sline")) {
2815 seq_error(seq, c, "Missing line number");
2816 return;
2817 }
2818 if (mxml_get_attribute(pn, "line")) {
2819 seq.current_line_number = atoi(eval_var(seq, c, mxml_get_attribute(pn, "line")).c_str());
2820 }
2821 if (mxml_get_attribute(pn, "sline")) {
2822 mstrlcpy(str, eval_var(seq, c, mxml_get_attribute(pn, "sline")).c_str(), sizeof(str));
2823 for (i = 0; i < last_line; i++) {
2824 pt = mxml_get_node_at_line(c->pnseq, i);
2825 if (pt && mxml_get_attribute(pt, "l")) {
2826 l = atoi(mxml_get_attribute(pt, "l"));
2827 if (atoi(str) == l) {
2828 seq.current_line_number = i;
2829 break;
2830 }
2831 }
2832 }
2833 }
2834 }
2835
2836 /*---- Library ----*/
2837 else if (equal_ustring(mxml_get_name(pn), "Library")) {
2838 // simply skip libraries
2839 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2840 }
2841
2842 /*---- Subroutine ----*/
2843 else if (equal_ustring(mxml_get_name(pn), "Subroutine")) {
2844 // simply skip subroutines
2845 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2846 }
2847
2848 /*---- Param ----*/
2849 else if (equal_ustring(mxml_get_name(pn), "Param")) {
2850 // simply skip parameters
2851 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2852 }
2853
2854 /*---- Set ----*/
2855 else if (equal_ustring(mxml_get_name(pn), "Set")) {
2856 if (!mxml_get_attribute(pn, "name")) {
2857 seq_error(seq, c, "Missing variable name");
2858 return;
2859 }
2860 mstrlcpy(name, mxml_get_attribute(pn, "name"), sizeof(name));
2861 value = eval_var(seq, c, mxml_get_value(pn));
2862
2863 if (strchr(name, '[')) {
2864 // array
2865 mstrlcpy(str, strchr(name, '[')+1, sizeof(str));
2866 if (strchr(str, ']'))
2867 *strchr(str, ']') = 0;
2868 int index = atoi(eval_var(seq, c, str).c_str());
2869 *strchr(name, '[') = 0;
2870 sprintf(str, "Variables/%s", name); // FIXME: unsafe
2871 status = db_find_key(c->hDB, c->hSeq, str, &hKey);
2872 if (status != DB_SUCCESS) {
2873 db_create_key(c->hDB, c->hSeq, str, TID_STRING);
2874 status = db_find_key(c->hDB, c->hSeq, str, &hKey);
2875 }
2876 size = value.length() + 1;
2877 if (size < 32)
2878 size = 32;
2879 status = db_set_data_index(c->hDB, hKey, value.c_str(), size, index, TID_STRING);
2880 } else {
2881 sprintf(str, "Variables/%s", name); // FIXME: unsafe
2882 size = value.length() + 1;
2883 if (size < 32)
2884 size = 32;
2885 db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
2886 }
2887
2888 // check if variable is used in loop
2889 for (i = SEQ_NEST_LEVEL_LOOP-1; i >= 0; i--)
2890 if (seq.loop_start_line[i] > 0) {
2891 pr = mxml_get_node_at_line(c->pnseq, seq.loop_start_line[i]);
2892 if (mxml_get_attribute(pr, "var")) {
2893 if (equal_ustring(mxml_get_attribute(pr, "var"), name))
2894 seq.loop_counter[i] = atoi(value.c_str());
2895 }
2896 }
2897
2898 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2899 }
2900
2901 /*---- Message ----*/
2902 else if (equal_ustring(mxml_get_name(pn), "Message")) {
2903 if (strchr(mxml_get_value(pn), '$')) // evaluate message string if $ present
2904 value = eval_var(seq, c, mxml_get_value(pn));
2905 else
2906 value = mxml_get_value(pn); // treat message as string
2907 const char *wait_attr = mxml_get_attribute(pn, "wait");
2908 bool wait = false;
2909 if (wait_attr)
2910 wait = (atoi(wait_attr) == 1);
2911
2912 if (!wait) {
2913 // message with no wait: set seq.message and move on. we do not care if web page clears it
2914 mstrlcpy(seq.message, value.c_str(), sizeof(seq.message));
2915 seq.message_wait = FALSE;
2916 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
2917 } else {
2918 // message with wait
2919
2920 // if message_wait not set, we are here for the first time
2921 if (!seq.message_wait) {
2922 mstrlcpy(seq.message, value.c_str(), sizeof(seq.message));
2923 seq.message_wait = TRUE;
2924 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
2925 // wait
2926 return;
2927 } else {
2928 // message_wait is set, we have been here before
2929
2930 // if web page did not clear the message, keep waiting
2931 if (seq.message[0] != 0) {
2932 // wait
2933 return;
2934 }
2935
2936 // web page cleared the message, we are done with waiting
2937 seq.message_wait = false;
2938 }
2939 }
2940
2941 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2942 }
2943
2944 /*---- Msg ----*/
2945 else if (equal_ustring(mxml_get_name(pn), "Msg")) {
2946 if (strchr(mxml_get_value(pn), '$')) // evaluate message string if $ present
2947 value = eval_var(seq, c, mxml_get_value(pn));
2948 else
2949 value = mxml_get_value(pn); // treat message as string
2950 std::string type = "INFO";
2951 if (mxml_get_attribute(pn, "type"))
2952 type = std::string(mxml_get_attribute(pn, "type"));
2953
2954 if (type == "ERROR")
2955 cm_msg(MERROR, "sequencer", "%s", value.c_str());
2956 else if (type == "DEBUG")
2957 cm_msg(MDEBUG, "sequencer", "%s", value.c_str());
2958 else if (type == "LOG")
2959 cm_msg(MLOG, "sequencer", "%s", value.c_str());
2960 else if (type == "TALK")
2961 cm_msg(MTALK, "sequencer", "%s", value.c_str());
2962 else
2963 cm_msg(MINFO, "sequencer", "%s", value.c_str());
2964
2965 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2966 }
2967
2968 /*---- Cat ----*/
2969 else if (equal_ustring(mxml_get_name(pn), "Cat")) {
2970 if (!mxml_get_attribute(pn, "name")) {
2971 seq_error(seq, c, "Missing variable name");
2972 return;
2973 }
2974 mstrlcpy(name, mxml_get_attribute(pn, "name"), sizeof(name));
2975 if (!concatenate(seq, c, data, sizeof(data), mxml_get_value(pn)))
2976 return;
2977 sprintf(str, "Variables/%s", name); // FIXME: unsafe
2978 size = strlen(data) + 1;
2979 if (size < 32)
2980 size = 32;
2981 db_set_value(c->hDB, c->hSeq, str, data, size, 1, TID_STRING);
2982
2983 seq.current_line_number = mxml_get_line_number_end(pn) + 1;
2984 }
2985
2986 /*---- Call ----*/
2987 else if (equal_ustring(mxml_get_name(pn), "Call")) {
2988 if (seq.stack_index == SEQ_NEST_LEVEL_SUB) {
2989 seq_error(seq, c, "Maximum subroutine level exceeded");
2990 return;
2991 } else {
2992 // put current line number on stack
2993 seq.subroutine_call_line[seq.stack_index] = mxml_get_line_number_end(pn);
2994 seq.ssubroutine_call_line[seq.stack_index] = atoi(mxml_get_attribute(pn, "l"));
2995 seq.subroutine_return_line[seq.stack_index] = mxml_get_line_number_end(pn) + 1;
2996
2997 // search subroutine
2998 for (i = 1; i < mxml_get_line_number_end(mxml_find_node(c->pnseq, "RunSequence")); i++) {
2999 pt = mxml_get_node_at_line(c->pnseq, i);
3000 if (pt) {
3001 if (equal_ustring(mxml_get_name(pt), "Subroutine")) {
3002 if (equal_ustring(mxml_get_attribute(pt, "name"), mxml_get_attribute(pn, "name"))) {
3003 // put routine end line on end stack
3004 seq.subroutine_end_line[seq.stack_index] = mxml_get_line_number_end(pt);
3005 // go to first line of subroutine
3006 seq.current_line_number = mxml_get_line_number_start(pt) + 1;
3007 // put parameter(s) on stack
3008 if (mxml_get_value(pn)) {
3009 char p[256];
3010 if (strchr(mxml_get_value(pn), '$')) // evaluate message string if $ present
3011 mstrlcpy(p, eval_var(seq, c, mxml_get_value(pn)).c_str(), sizeof(p));
3012 else
3013 mstrlcpy(p, mxml_get_value(pn), sizeof(p)); // treat message as string
3014
3015 mstrlcpy(seq.subroutine_param[seq.stack_index], p, 256);
3016 }
3017 // increment stack
3018 seq.stack_index++;
3019 break;
3020 }
3021 }
3022 }
3023 }
3024 if (i == mxml_get_line_number_end(mxml_find_node(c->pnseq, "RunSequence"))) {
3025 seq_error(seq, c, msprintf("Subroutine '%s' not found", mxml_get_attribute(pn, "name")).c_str());
3026 }
3027 }
3028 }
3029
3030 /*---- <unknown> ----*/
3031 else {
3032 seq_error(seq, c, msprintf("Unknown statement \"%s\"", mxml_get_name(pn)).c_str());
3033 }
3034
3035 /* get steering parameters, since they might have been changed in between */
3036 SEQUENCER seq1;
3037 size = sizeof(seq1);
3038 db_get_record(c->hDB, hKeySeq, &seq1, &size, 0);
3039 seq.running = seq1.running;
3040 seq.finished = seq1.finished;
3041 seq.paused = seq1.paused;
3042 seq.debug = seq1.debug;
3043 seq.stop_after_run = seq1.stop_after_run;
3044 mstrlcpy(seq.message, seq1.message, sizeof(seq.message));
3045 memcpy(seq.next_filename, seq1.next_filename, sizeof(seq.next_filename));
3046
3047 // in debugging mode, pause after each step
3048 if (seq.debug && !skip_step)
3049 seq.paused = TRUE;
3050
3051 /* update current line number */
3052 db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
3053}
INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5304
#define CM_DEFERRED_TRANSITION
Definition midas.h:591
#define DB_INVALID_PARAM
Definition midas.h:640
#define DB_FILE_ERROR
Definition midas.h:648
#define DB_NO_KEY
Definition midas.h:643
#define DB_CREATED
Definition midas.h:633
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 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 STATE_RUNNING
Definition midas.h:307
#define MDEBUG
Definition midas.h:561
#define TR_STOP
Definition midas.h:406
#define TID_LAST
Definition midas.h:354
DWORD ss_millitime()
Definition system.cxx:3465
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_sleep(INT millisec)
Definition system.cxx:3700
INT cm_msg_retrieve(INT n_message, char *message, INT buf_size)
Definition midas.cxx:1350
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
Definition odb.cxx:6917
INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
Definition odb.cxx:3933
INT db_save_json(HNDLE hDB, HNDLE hKey, const char *filename, int flags)
Definition odb.cxx:10548
INT db_save_xml(HNDLE hDB, HNDLE hKey, const char *filename)
Definition odb.cxx:9503
INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
Definition odb.cxx:3392
INT db_get_record(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT align)
Definition odb.cxx:11736
INT db_save(HNDLE hDB, HNDLE hKey, const char *filename, BOOL bRemote)
Definition odb.cxx:9263
INT db_get_key(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6043
INT db_load(HNDLE hDB, HNDLE hKeyRoot, const char *filename, BOOL bRemote)
Definition odb.cxx:8139
INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7668
INT db_delete(HNDLE hDB, HNDLE hKeyRoot, const char *odb_path)
Definition odb.cxx:3999
INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10865
INT db_set_data_index1(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type, BOOL bNotify)
Definition odb.cxx:7844
INT db_sscanf(const char *data_str, void *data, INT *data_size, INT i, DWORD tid)
Definition odb.cxx:11336
INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
Definition odb.cxx:7523
const char * rpc_tid_name(INT id)
Definition midas.cxx:11895
void seq_array_index(SEQUENCER &seq, SeqCon *c, char *odbpath, int *index1, int *index2)
int concatenate(SEQUENCER &seq, SeqCon *c, char *result, int size, char *value)
static void seq_start_next(SEQUENCER &seq, SeqCon *c)
int set_all_matching(HNDLE hDB, HNDLE hBaseKey, const char *odbpath, char *value, int index1, int index2, int notify)
int eval_condition(SEQUENCER &seq, SeqCon *c, const char *condition)
INT run_number[2]
Definition mana.cxx:246
void * data
Definition mana.cxx:268
INT type
Definition mana.cxx:269
KEY key
Definition mdump.cxx:34
#define DWORD
Definition mhdump.cxx:31
#define JSFLAG_RECURSE
Definition midas.h:1712
#define JSFLAG_FOLLOW_LINKS
Definition midas.h:1711
#define JSFLAG_OMIT_LAST_WRITTEN
Definition midas.h:1715
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 if_line[SEQ_NEST_LEVEL_IF]
Definition sequencer.h:37
char last_msg[10]
Definition sequencer.h:53
static void pn(const te_expr *n, int depth)
Definition tinyexpr.c:702
Here is the call graph for this function:
Here is the caller graph for this function:

◆ SEQUENCER_STR()

SEQUENCER_STR ( sequencer_str  )

◆ set_all_matching()

int set_all_matching ( HNDLE  hDB,
HNDLE  hBaseKey,
const char *  odbpath,
char *  value,
int  index1,
int  index2,
int  notify 
)

Definition at line 1668 of file msequencer.cxx.

1668 {
1669 int status, size;
1670 char data[256];
1671 KEY key;
1672
1673 std::vector<HNDLE> keys;
1674 status = db_find_keys(hDB, hBaseKey, odbpath, keys);
1675
1676 if (status != DB_SUCCESS)
1677 return status;
1678
1679 for (HNDLE hKey: keys) {
1680 db_get_key(hDB, hKey, &key);
1681 size = sizeof(data);
1682 db_sscanf(value, data, &size, 0, key.type);
1683
1684 /* extend data size for single string if necessary */
1685 if ((key.type == TID_STRING || key.type == TID_LINK)
1686 && (int) strlen(data) + 1 > key.item_size && key.num_values == 1)
1687 key.item_size = strlen(data) + 1;
1688
1689 if (key.num_values > 1 && index1 == -1) {
1690 for (int i = 0; i < key.num_values; i++)
1692 } else if (key.num_values > 1 && index2 > index1) {
1693 if (index1 < 0 || index1 >= key.num_values || index2 < 0 || index2 >= key.num_values)
1694 return DB_INVALID_PARAM;
1695 for (int i = index1; i < key.num_values && i <= index2; i++)
1697 } else {
1698 if (index1 < 0 || index1 >= key.num_values)
1699 return DB_INVALID_PARAM;
1700 status = db_set_data_index1(hDB, hKey, data, key.item_size, index1, key.type, notify);
1701 }
1702
1703 if (status != DB_SUCCESS) {
1704 return status;
1705 }
1706 }
1707
1708 return DB_SUCCESS;
1709}
INT db_find_keys(HNDLE hDB, HNDLE hKeyRoot, const char *odbpath, std::vector< HNDLE > &hKeyVector)
Definition odb.cxx:4352
Here is the call graph for this function:
Here is the caller graph for this function:

◆ strbreak()

int strbreak ( char *  str,
char  list[][XNAME_LENGTH],
int  size,
const char *  brk,
BOOL  ignore_quotes 
)

Definition at line 186 of file msequencer.cxx.

189{
190 int i, j;
191 char *p;
192
193 memset(list, 0, size * XNAME_LENGTH);
194 p = str;
195 if (!p || !*p)
196 return 0;
197
198 while (*p == ' ')
199 p++;
200
201 for (i = 0; *p && i < size; i++) {
202 if (*p == '"' && !ignore_quotes) {
203 p++;
204 j = 0;
205 memset(list[i], 0, XNAME_LENGTH);
206 do {
207 /* convert two '"' to one */
208 if (*p == '"' && *(p + 1) == '"') {
209 list[i][j++] = '"';
210 p += 2;
211 } else if (*p == '"') {
212 break;
213 } else
214 list[i][j++] = *p++;
215
216 } while (j < XNAME_LENGTH - 1);
217 list[i][j] = 0;
218
219 /* skip second '"' */
220 p++;
221
222 /* skip blanks and break character */
223 while (*p == ' ')
224 p++;
225 if (*p && strchr(brk, *p))
226 p++;
227 while (*p == ' ')
228 p++;
229
230 } else {
231 mstrlcpy(list[i], p, XNAME_LENGTH);
232
233 for (j = 0; j < (int) strlen(list[i]); j++)
234 if (strchr(brk, list[i][j])) {
235 list[i][j] = 0;
236 break;
237 }
238
239 p += strlen(list[i]);
240 while (*p == ' ')
241 p++;
242 if (*p && strchr(brk, *p))
243 p++;
244 while (*p == ' ')
245 p++;
246
247 while (list[i][strlen(list[i]) - 1] == ' ')
248 list[i][strlen(list[i]) - 1] = 0;
249 }
250
251 if (!*p)
252 break;
253 }
254
255 if (i == size)
256 return size;
257
258 return i + 1;
259}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ stristr()

char * stristr ( const char *  str,
const char *  pattern 
)

Definition at line 63 of file msequencer.cxx.

63 {
64 char c1, c2, *ps, *pp;
65
66 if (str == NULL || pattern == NULL)
67 return NULL;
68
69 while (*str) {
70 ps = (char *) str;
71 pp = (char *) pattern;
72 c1 = *ps;
73 c2 = *pp;
74 if (toupper(c1) == toupper(c2)) {
75 while (*pp) {
76 c1 = *ps;
77 c2 = *pp;
78
79 if (toupper(c1) != toupper(c2))
80 break;
81
82 ps++;
83 pp++;
84 }
85
86 if (!*pp)
87 return (char *) str;
88 }
89 str++;
90 }
91
92 return NULL;
93}
Here is the caller graph for this function:

◆ strsubst()

void strsubst ( char *  string,
int  size,
const char *  pattern,
const char *  subst 
)

Definition at line 97 of file msequencer.cxx.

99{
100 char *tail, *p;
101 int s;
102
103 p = string;
104 for (p = stristr(p, pattern); p != NULL; p = stristr(p, pattern)) {
105
106 if (strlen(pattern) == strlen(subst)) {
107 memcpy(p, subst, strlen(subst));
108 } else if (strlen(pattern) > strlen(subst)) {
109 memcpy(p, subst, strlen(subst));
110 memmove(p + strlen(subst), p + strlen(pattern), strlen(p + strlen(pattern)) + 1);
111 } else {
112 tail = (char *) malloc(strlen(p) - strlen(pattern) + 1);
113 strcpy(tail, p + strlen(pattern));
114 s = size - (p - string);
115 mstrlcpy(p, subst, s);
116 mstrlcat(p, tail, s);
117 free(tail);
118 tail = NULL;
119 }
120
121 p += strlen(subst);
122 }
123}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ toString()

static std::string toString ( int  v)
static

Definition at line 127 of file msequencer.cxx.

127 {
128 char buf[256];
129 snprintf(buf, sizeof(buf), "%d", v);
130 return buf;
131}

Variable Documentation

◆ gOdb

MVOdb* gOdb = NULL

Definition at line 44 of file msequencer.cxx.