Line data Source code
1 : /********************************************************************\
2 :
3 : Name: ALARM.C
4 : Created by: Stefan Ritt
5 :
6 : Contents: MIDAS alarm functions
7 :
8 : $Id$
9 :
10 : \********************************************************************/
11 :
12 : #include "midas.h"
13 : #include "msystem.h"
14 : #include "mstrlcpy.h"
15 : #include "odbxx.h"
16 : #include <assert.h>
17 :
18 : /**dox***************************************************************/
19 : /** @file alarm.c
20 : The Midas Alarm file
21 : */
22 :
23 : /** @defgroup alfunctioncode Midas Alarm Functions (al_xxx)
24 : */
25 :
26 : /**dox***************************************************************/
27 : /** @addtogroup alfunctioncode
28 : *
29 : * @{ */
30 :
31 : /**dox***************************************************************/
32 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
33 :
34 : /********************************************************************\
35 : * *
36 : * Alarm functions *
37 : * *
38 : \********************************************************************/
39 :
40 : /********************************************************************/
41 0 : BOOL al_evaluate_condition(const char* alarm_name, const char *condition, std::string *pvalue)
42 : {
43 : int i, j, size, status;
44 :
45 : //char str[256];
46 : //strcpy(str, condition);
47 :
48 0 : std::vector<char> buf;
49 0 : buf.insert(buf.end(), condition, condition+strlen(condition)+1);
50 0 : char* str = buf.data();
51 :
52 : /* find value and operator */
53 : char op[3];
54 0 : op[0] = op[1] = op[2] = 0;
55 0 : for (i = strlen(str) - 1; i > 0; i--)
56 0 : if (strchr("<>=!&", str[i]) != NULL)
57 0 : break;
58 0 : op[0] = str[i];
59 0 : for (j = 1; str[i + j] == ' '; j++)
60 : ;
61 :
62 0 : std::string value2_str = str + i + j;
63 0 : double value2 = atof(value2_str.c_str());
64 :
65 0 : str[i] = 0;
66 :
67 0 : if (i > 0 && strchr("<>=!&", str[i - 1])) {
68 0 : op[1] = op[0];
69 0 : op[0] = str[--i];
70 0 : str[i] = 0;
71 : }
72 :
73 0 : i--;
74 0 : while (i > 0 && str[i] == ' ')
75 0 : i--;
76 0 : str[i + 1] = 0;
77 :
78 : /* check if function */
79 0 : std::string function;
80 0 : if (str[i] == ')') {
81 0 : str[i--] = 0;
82 0 : if (strchr(str, '(')) {
83 0 : *strchr(str, '(') = 0;
84 0 : function = str;
85 0 : for (i = strlen(str) + 1, j = 0; str[i]; i++, j++)
86 0 : str[j] = str[i];
87 0 : str[j] = 0;
88 0 : i = j - 1;
89 : }
90 : }
91 :
92 0 : std::vector<int> indices;
93 0 : bool index_present = false;
94 :
95 : // process indices
96 0 : if (strchr(str, '[') && str[strlen(str)-1] == ']') {
97 0 : index_present = true;
98 :
99 : // Remove the square brackets from the string
100 0 : std::string cleanInput = strchr(str, '[') + 1;
101 0 : cleanInput.pop_back();
102 :
103 : // Remove index from str
104 0 : *strchr(str, '[') = 0;
105 :
106 : // retrieve key
107 0 : if (!midas::odb::exists(str)) {
108 0 : cm_msg(MERROR, "al_evaluate_condition", "Alarm \"%s\": Cannot find ODB key \"%s\" to evaluate alarm condition", alarm_name, str);
109 0 : if (pvalue)
110 0 : *pvalue = "(cannot find in odb)";
111 0 : return FALSE;
112 : }
113 0 : midas::odb k((const char *)str);
114 :
115 : // Split the string by commas
116 0 : std::stringstream ss(cleanInput);
117 0 : std::string token;
118 0 : while (std::getline(ss, token, ',')) {
119 : // Remove whitespace
120 0 : token.erase(std::remove_if(token.begin(), token.end(), ::isspace), token.end());
121 :
122 : // Check if token is "*", if so set all indices
123 0 : if (token == "*") {
124 0 : for (int i = 0; i < k.get_num_values(); ++i)
125 0 : indices.push_back(i);
126 : } else {
127 : // Check if the token represents a range (e.g., "4-7")
128 0 : auto dashPos = token.find('-');
129 0 : if (dashPos != std::string::npos) {
130 : // Extract the start and end of the range
131 0 : int start = std::stoi(token.substr(0, dashPos));
132 0 : int end = std::stoi(token.substr(dashPos + 1));
133 :
134 : // Add all numbers in the range [start, end]
135 0 : for (int i = start; i <= end; ++i) {
136 0 : indices.push_back(i);
137 : }
138 : } else {
139 : // It's a single number, add it to the list
140 0 : indices.push_back(std::stoi(token));
141 : }
142 : }
143 : }
144 0 : }
145 :
146 : HNDLE hDB, hkey;
147 0 : cm_get_experiment_database(&hDB, NULL);
148 0 : db_find_key(hDB, 0, str, &hkey);
149 0 : if (!hkey) {
150 0 : cm_msg(MERROR, "al_evaluate_condition", "Alarm \"%s\": Cannot find ODB key \"%s\" to evaluate alarm condition", alarm_name, str);
151 0 : if (pvalue)
152 0 : *pvalue = "(cannot find in odb)";
153 0 : return FALSE;
154 : }
155 :
156 : KEY key;
157 0 : db_get_key(hDB, hkey, &key);
158 :
159 : //printf("Alarm \"%s\": [%s], idx1 %d, idx2 %d, op [%s], value2 [%s] %g, function [%s]\n", alarm_name, condition, idx1, idx2, op, value2_str.c_str(), value2, function.c_str());
160 :
161 : // put at least one value into the indices
162 0 : if (indices.empty())
163 0 : indices.push_back(0);
164 :
165 : // go through all indices
166 0 : for (int idx : indices) {
167 0 : std::string value1_str;
168 0 : double value1 = 0;
169 :
170 0 : if (equal_ustring(function.c_str(), "access")) {
171 : /* check key access time */
172 : DWORD dtime;
173 0 : db_get_key_time(hDB, hkey, &dtime);
174 0 : value1_str = msprintf("%d", dtime);
175 0 : value1 = atof(value1_str.c_str());
176 0 : } else if (equal_ustring(function.c_str(), "access_running")) {
177 : /* check key access time if running */
178 : DWORD dtime;
179 0 : db_get_key_time(hDB, hkey, &dtime);
180 0 : int state = 0;
181 0 : size = sizeof(state);
182 0 : db_get_value(hDB, 0, "/Runinfo/State", &state, &size, TID_INT, FALSE);
183 0 : if (state == STATE_RUNNING) {
184 0 : value1_str = msprintf("%d", dtime).c_str();
185 0 : value1 = atof(value1_str.c_str());
186 : } else {
187 0 : value1_str = "0";
188 0 : value1 = 0;
189 : }
190 : } else {
191 : /* get key data and convert to double */
192 0 : status = db_get_key(hDB, hkey, &key);
193 0 : if (status != DB_SUCCESS) {
194 0 : cm_msg(MERROR, "al_evaluate_condition", "Alarm \"%s\": Cannot get ODB key \"%s\" to evaluate alarm condition", alarm_name, str);
195 0 : if (pvalue)
196 0 : *pvalue = "(odb error)";
197 0 : return FALSE;
198 : } else {
199 : char data[256];
200 0 : size = sizeof(data);
201 0 : status = db_get_data_index(hDB, hkey, data, &size, idx, key.type);
202 0 : if (status != DB_SUCCESS) {
203 0 : cm_msg(MERROR, "al_evaluate_condition", "Alarm \"%s\": Cannot get ODB value \"%s\" index %d to evaluate alarm condition", alarm_name, str, idx);
204 0 : if (pvalue)
205 0 : *pvalue = "(odb error)";
206 0 : return FALSE;
207 : }
208 :
209 0 : value1_str = db_sprintf(data, size, 0, key.type);
210 0 : value1 = atof(value1_str.c_str());
211 : }
212 : }
213 :
214 : /* convert boolean values to integers */
215 0 : if (key.type == TID_BOOL) {
216 0 : value1 = (value1_str[0] == 'Y' || value1_str[0] == 'y' || value1_str[0] == '1');
217 0 : value2 = (value2_str[0] == 'Y' || value2_str[0] == 'y' || value2_str[0] == '1');
218 : }
219 :
220 : /* return value */
221 0 : if (pvalue) {
222 0 : if (index_present)
223 0 : *pvalue = msprintf("[%d] %s", idx, value1_str.c_str());
224 : else
225 0 : *pvalue = value1_str;
226 : }
227 :
228 : /* now do logical operation */
229 0 : if (strcmp(op, "=") == 0)
230 0 : if (value1 == value2)
231 0 : return TRUE;
232 0 : if (strcmp(op, "==") == 0)
233 0 : if (value1 == value2)
234 0 : return TRUE;
235 0 : if (strcmp(op, "!=") == 0)
236 0 : if (value1 != value2)
237 0 : return TRUE;
238 0 : if (strcmp(op, "<") == 0)
239 0 : if (value1 < value2)
240 0 : return TRUE;
241 0 : if (strcmp(op, ">") == 0)
242 0 : if (value1 > value2)
243 0 : return TRUE;
244 0 : if (strcmp(op, "<=") == 0)
245 0 : if (value1 <= value2)
246 0 : return TRUE;
247 0 : if (strcmp(op, ">=") == 0)
248 0 : if (value1 >= value2)
249 0 : return TRUE;
250 0 : if (strcmp(op, "&") == 0)
251 0 : if (((unsigned int) value1 & (unsigned int) value2) > 0)
252 0 : return TRUE;
253 0 : }
254 :
255 0 : return FALSE;
256 0 : }
257 :
258 : /**dox***************************************************************/
259 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
260 :
261 : /********************************************************************/
262 : /**
263 : Trigger a certain alarm.
264 : \code ...
265 : lazy.alarm[0] = 0;
266 : size = sizeof(lazy.alarm);
267 : db_get_value(hDB, pLch->hKey, "Settings/Alarm Class", lazy.alarm, &size, TID_STRING, TRUE);
268 :
269 : // trigger alarm if defined
270 : if (lazy.alarm[0])
271 : al_trigger_alarm("Tape", "Tape full...load new one!", lazy.alarm, "Tape full", AT_INTERNAL);
272 : ...
273 : \endcode
274 : @param alarm_name Alarm name, defined in /alarms/alarms
275 : @param alarm_message Optional message which goes with alarm
276 : @param default_class If alarm is not yet defined under
277 : /alarms/alarms/\<alarm_name\>, a new one
278 : is created and this default class is used.
279 : @param cond_str String displayed in alarm condition
280 : @param type Alarm type, one of AT_xxx
281 : @return AL_SUCCESS, AL_INVALID_NAME
282 : */
283 0 : INT al_trigger_alarm(const char *alarm_name, const char *alarm_message, const char *default_class, const char *cond_str,
284 : INT type) {
285 0 : if (rpc_is_remote())
286 0 : return rpc_call(RPC_AL_TRIGGER_ALARM, alarm_name, alarm_message, default_class, cond_str, type);
287 :
288 : #ifdef LOCAL_ROUTINES
289 : {
290 : int status, size;
291 : HNDLE hDB, hkeyalarm, hkey;
292 : ALARM a;
293 : BOOL flag;
294 0 : ALARM_ODB_STR(alarm_odb_str);
295 :
296 0 : cm_get_experiment_database(&hDB, NULL);
297 :
298 : /* check online mode */
299 0 : flag = TRUE;
300 0 : size = sizeof(flag);
301 0 : db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
302 0 : if (!flag)
303 0 : return AL_SUCCESS;
304 :
305 : /* find alarm */
306 0 : std::string alarm_path = msprintf("/Alarms/Alarms/%s", alarm_name);
307 :
308 0 : db_find_key(hDB, 0, alarm_path.c_str(), &hkeyalarm);
309 0 : if (!hkeyalarm) {
310 : /* alarm must be an internal analyzer alarm, so create a default alarm */
311 0 : status = db_create_record(hDB, 0, alarm_path.c_str(), strcomb1(alarm_odb_str).c_str());
312 0 : db_find_key(hDB, 0, alarm_path.c_str(), &hkeyalarm);
313 0 : if (!hkeyalarm) {
314 0 : cm_msg(MERROR, "al_trigger_alarm", "Cannot create alarm record for alarm \"%s\", db_create_record() status %d", alarm_path.c_str(), status);
315 0 : return AL_ERROR_ODB;
316 : }
317 :
318 0 : if (default_class && default_class[0])
319 0 : db_set_value(hDB, hkeyalarm, "Alarm Class", default_class, 32, 1, TID_STRING);
320 0 : status = TRUE;
321 0 : db_set_value(hDB, hkeyalarm, "Active", &status, sizeof(status), 1, TID_BOOL);
322 : }
323 :
324 : /* set parameters for internal alarms */
325 0 : if (type != AT_EVALUATED && type != AT_PERIODIC) {
326 0 : db_set_value(hDB, hkeyalarm, "Type", &type, sizeof(INT), 1, TID_INT);
327 : char str[256];
328 0 : mstrlcpy(str, cond_str, sizeof(str));
329 0 : db_set_value(hDB, hkeyalarm, "Condition", str, 256, 1, TID_STRING);
330 : }
331 :
332 0 : size = sizeof(a);
333 0 : status = db_get_record1(hDB, hkeyalarm, &a, &size, 0, strcomb1(alarm_odb_str).c_str());
334 0 : if (status != DB_SUCCESS || a.type < 1 || a.type > AT_LAST) {
335 : /* make sure alarm record has right structure */
336 0 : size = sizeof(a);
337 0 : status = db_get_record1(hDB, hkeyalarm, &a, &size, 0, strcomb1(alarm_odb_str).c_str());
338 0 : if (status != DB_SUCCESS) {
339 0 : cm_msg(MERROR, "al_trigger_alarm", "Cannot get alarm record for alarm \"%s\", db_get_record1() status %d", alarm_path.c_str(), status);
340 0 : return AL_ERROR_ODB;
341 : }
342 : }
343 :
344 : /* if internal alarm, check if active and check interval */
345 0 : if (a.type != AT_EVALUATED && a.type != AT_PERIODIC) {
346 : /* check global alarm flag */
347 0 : flag = TRUE;
348 0 : size = sizeof(flag);
349 0 : db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL, TRUE);
350 0 : if (!flag)
351 0 : return AL_SUCCESS;
352 :
353 0 : if (!a.active)
354 0 : return AL_SUCCESS;
355 :
356 0 : if ((INT) ss_time() - (INT) a.checked_last < a.check_interval)
357 0 : return AL_SUCCESS;
358 :
359 : /* now the alarm will be triggered, so save time */
360 0 : a.checked_last = ss_time();
361 : }
362 :
363 0 : if (a.type == AT_PROGRAM) {
364 : /* alarm class of "program not running" alarms is set in two places,
365 : * complain if they do not match */
366 0 : if (!equal_ustring(a.alarm_class, default_class)) {
367 0 : cm_msg(MERROR, "al_trigger_alarm",
368 : "Program alarm class mismatch: \"%s/Alarm class\" set to \"%s\" does not match \"/Programs/%s/Alarm "
369 : "Class\" set to \"%s\"",
370 : alarm_path.c_str(), a.alarm_class, alarm_name, default_class);
371 : }
372 : }
373 :
374 : /* write back alarm message for internal alarms */
375 0 : if (a.type != AT_EVALUATED && a.type != AT_PERIODIC) {
376 0 : mstrlcpy(a.alarm_message, alarm_message, sizeof(a.alarm_message));
377 : }
378 :
379 : /* now trigger alarm class defined in this alarm */
380 0 : if (a.alarm_class[0])
381 0 : al_trigger_class(a.alarm_class, alarm_message, a.triggered > 0);
382 :
383 : /* check for and trigger "All" class */
384 0 : if (db_find_key(hDB, 0, "/Alarms/Classes/All", &hkey) == DB_SUCCESS)
385 0 : al_trigger_class("All", alarm_message, a.triggered > 0);
386 :
387 : /* signal alarm being triggered */
388 0 : std::string now = cm_asctime();
389 :
390 0 : if (!a.triggered)
391 0 : mstrlcpy(a.time_triggered_first, now.c_str(), sizeof(a.time_triggered_first));
392 :
393 0 : a.triggered++;
394 0 : mstrlcpy(a.time_triggered_last, now.c_str(), sizeof(a.time_triggered_last));
395 :
396 0 : a.checked_last = ss_time();
397 :
398 0 : status = db_set_record(hDB, hkeyalarm, &a, sizeof(a), 0);
399 0 : if (status != DB_SUCCESS) {
400 0 : cm_msg(MERROR, "al_trigger_alarm", "Cannot update alarm record for alarm \"%s\", db_set_record() status %d", alarm_path.c_str(), status);
401 0 : return AL_ERROR_ODB;
402 : }
403 0 : }
404 : #endif /* LOCAL_ROUTINES */
405 :
406 0 : return AL_SUCCESS;
407 : }
408 :
409 : /**dox***************************************************************/
410 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
411 :
412 : /********************************************************************/
413 0 : INT al_trigger_class(const char *alarm_class, const char *alarm_message, BOOL first)
414 : /********************************************************************\
415 :
416 : Routine: al_trigger_class
417 :
418 : Purpose: Trigger a certain alarm class
419 :
420 : Input:
421 : char *alarm_class Alarm class, must be defined in
422 : /alarms/classes
423 : char *alarm_message Optional message which goes with alarm
424 : BOOL first TRUE if alarm is triggered first time
425 : (used for elog)
426 :
427 : Output:
428 :
429 : Function value:
430 : AL_INVALID_NAME Alarm class not defined
431 : AL_SUCCESS Successful completion
432 :
433 : \********************************************************************/
434 : {
435 : int status, size, state;
436 : HNDLE hDB, hkeyclass;
437 : ALARM_CLASS ac;
438 0 : ALARM_CLASS_STR(alarm_class_str);
439 0 : DWORD now = ss_time();
440 :
441 0 : cm_get_experiment_database(&hDB, NULL);
442 :
443 : /* get alarm class */
444 0 : std::string alarm_path = msprintf("/Alarms/Classes/%s", alarm_class);
445 0 : db_find_key(hDB, 0, alarm_path.c_str(), &hkeyclass);
446 0 : if (!hkeyclass) {
447 0 : cm_msg(MERROR, "al_trigger_class", "Alarm class \"%s\" for alarm \"%s\" not found in ODB", alarm_class, alarm_message);
448 0 : return AL_INVALID_NAME;
449 : }
450 :
451 0 : size = sizeof(ac);
452 0 : status = db_get_record1(hDB, hkeyclass, &ac, &size, 0, strcomb1(alarm_class_str).c_str());
453 0 : if (status != DB_SUCCESS) {
454 0 : cm_msg(MERROR, "al_trigger_class", "Cannot get alarm class record \"%s\", db_get_record1() status %d", alarm_path.c_str(), status);
455 0 : return AL_ERROR_ODB;
456 : }
457 :
458 0 : std::string msg;
459 :
460 : /* write system message */
461 0 : if (ac.write_system_message && (now - ac.system_message_last >= (DWORD) ac.system_message_interval)) {
462 0 : if (equal_ustring(alarm_class, "All"))
463 0 : msg = msprintf("General alarm: %s", alarm_message);
464 : else
465 0 : msg = msprintf("%s: %s", alarm_class, alarm_message);
466 0 : cm_msg(MTALK, "al_trigger_class", "%s", msg.c_str());
467 0 : ac.system_message_last = now;
468 : }
469 :
470 : /* write elog message on first trigger if using internal ELOG */
471 0 : if (ac.write_elog_message && first) {
472 0 : BOOL external_elog = FALSE;
473 0 : size = sizeof(external_elog);
474 0 : db_get_value(hDB, 0, "/Elog/External Elog", &external_elog, &size, TID_BOOL, FALSE);
475 0 : if (!external_elog) {
476 : char tag[32];
477 0 : tag[0] = 0;
478 0 : el_submit(0, "Alarm system", "Alarm", "General", alarm_class, msg.c_str(), "", "plain", "", "", 0, "", "", 0, "", "", 0, tag, sizeof(tag));
479 : }
480 : }
481 :
482 : /* execute command */
483 0 : if (ac.execute_command[0] && ac.execute_interval > 0 && (INT) ss_time() - (INT) ac.execute_last > ac.execute_interval) {
484 0 : if (equal_ustring(alarm_class, "All"))
485 0 : msg = msprintf("General alarm: %s", alarm_message);
486 : else
487 0 : msg = msprintf("%s: %s", alarm_class, alarm_message);
488 0 : std::string command = msprintf(ac.execute_command, msg.c_str());
489 0 : cm_msg(MINFO, "al_trigger_class", "Execute: %s", command.c_str());
490 0 : ss_system(command.c_str());
491 0 : ac.execute_last = ss_time();
492 0 : }
493 :
494 : /* stop run */
495 0 : if (ac.stop_run) {
496 0 : state = STATE_STOPPED;
497 0 : size = sizeof(state);
498 0 : db_get_value(hDB, 0, "/Runinfo/State", &state, &size, TID_INT, TRUE);
499 0 : if (state != STATE_STOPPED) {
500 0 : cm_msg(MINFO, "al_trigger_class", "Stopping the run from alarm class \'%s\', message \'%s\'", alarm_class,
501 : alarm_message);
502 0 : cm_transition(TR_STOP, 0, NULL, 0, TR_DETACH, FALSE);
503 : }
504 : }
505 :
506 0 : status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
507 0 : if (status != DB_SUCCESS) {
508 0 : cm_msg(MERROR, "al_trigger_class", "Cannot update alarm class record");
509 0 : return AL_ERROR_ODB;
510 : }
511 :
512 0 : return AL_SUCCESS;
513 0 : }
514 :
515 : /**dox***************************************************************/
516 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
517 :
518 : /********************************************************************/
519 : /**
520 : Reset (acknoledge) alarm.
521 :
522 : @param alarm_name Alarm name, defined in /alarms/alarms
523 : @return AL_SUCCESS, AL_RESETE, AL_INVALID_NAME
524 : */
525 0 : INT al_reset_alarm(const char *alarm_name) {
526 : int status, size, i;
527 : HNDLE hDB, hkeyalarm, hkeyclass, hsubkey;
528 : KEY key;
529 : ALARM a;
530 : ALARM_CLASS ac;
531 0 : ALARM_ODB_STR(alarm_str);
532 0 : ALARM_CLASS_STR(alarm_class_str);
533 :
534 0 : cm_get_experiment_database(&hDB, NULL);
535 :
536 0 : if (alarm_name == NULL) {
537 : /* reset all alarms */
538 0 : db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyalarm);
539 0 : if (hkeyalarm) {
540 0 : for (i = 0;; i++) {
541 0 : db_enum_link(hDB, hkeyalarm, i, &hsubkey);
542 :
543 0 : if (!hsubkey)
544 0 : break;
545 :
546 0 : db_get_key(hDB, hsubkey, &key);
547 0 : al_reset_alarm(key.name);
548 : }
549 : }
550 0 : return AL_SUCCESS;
551 : }
552 :
553 : /* find alarm and alarm class */
554 0 : std::string str = msprintf("/Alarms/Alarms/%s", alarm_name);
555 0 : db_find_key(hDB, 0, str.c_str(), &hkeyalarm);
556 0 : if (!hkeyalarm) {
557 : /*cm_msg(MERROR, "al_reset_alarm", "Alarm %s not found in ODB", alarm_name);*/
558 0 : return AL_INVALID_NAME;
559 : }
560 :
561 0 : size = sizeof(a);
562 0 : status = db_get_record1(hDB, hkeyalarm, &a, &size, 0, strcomb1(alarm_str).c_str());
563 0 : if (status != DB_SUCCESS) {
564 0 : cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm record");
565 0 : return AL_ERROR_ODB;
566 : }
567 :
568 0 : str = msprintf("/Alarms/Classes/%s", a.alarm_class);
569 0 : db_find_key(hDB, 0, str.c_str(), &hkeyclass);
570 0 : if (!hkeyclass) {
571 0 : cm_msg(MERROR, "al_reset_alarm", "Alarm class %s not found in ODB", a.alarm_class);
572 0 : return AL_INVALID_NAME;
573 : }
574 :
575 0 : size = sizeof(ac);
576 0 : status = db_get_record1(hDB, hkeyclass, &ac, &size, 0, strcomb1(alarm_class_str).c_str());
577 0 : if (status != DB_SUCCESS) {
578 0 : cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm class record");
579 0 : return AL_ERROR_ODB;
580 : }
581 :
582 0 : if (a.triggered) {
583 0 : a.triggered = 0;
584 0 : a.time_triggered_first[0] = 0;
585 0 : a.time_triggered_last[0] = 0;
586 0 : a.checked_last = 0;
587 0 : a.trigger_count = 0;
588 :
589 0 : ac.system_message_last = 0;
590 0 : ac.execute_last = 0;
591 :
592 0 : status = db_set_record(hDB, hkeyalarm, &a, sizeof(a), 0);
593 0 : if (status != DB_SUCCESS) {
594 0 : cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm record");
595 0 : return AL_ERROR_ODB;
596 : }
597 0 : status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
598 0 : if (status != DB_SUCCESS) {
599 0 : cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm class record");
600 0 : return AL_ERROR_ODB;
601 : }
602 0 : cm_msg(MINFO, "al_reset_alarm", "Alarm \"%s\" reset", alarm_name);
603 0 : return AL_RESET;
604 : }
605 :
606 0 : return AL_SUCCESS;
607 0 : }
608 :
609 : /********************************************************************/
610 : /**
611 : Scan ODB for alarms.
612 : @return AL_SUCCESS
613 : */
614 0 : INT al_check() {
615 0 : if (rpc_is_remote())
616 0 : return rpc_call(RPC_AL_CHECK);
617 :
618 : #ifdef LOCAL_ROUTINES
619 : {
620 : INT status, size, semaphore;
621 : HNDLE hDB, hkeyroot, hkey;
622 : KEY key;
623 : //char str[256];
624 0 : PROGRAM_INFO_STR(program_info_str);
625 : PROGRAM_INFO program_info;
626 : BOOL flag;
627 :
628 0 : ALARM_CLASS_STR(alarm_class_str);
629 0 : ALARM_ODB_STR(alarm_odb_str);
630 0 : ALARM_PERIODIC_STR(alarm_periodic_str);
631 :
632 0 : cm_get_experiment_database(&hDB, NULL);
633 :
634 0 : if (hDB == 0)
635 0 : return AL_SUCCESS; /* called from server not yet connected */
636 :
637 : /* check online mode */
638 0 : flag = TRUE;
639 0 : size = sizeof(flag);
640 0 : db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
641 0 : if (!flag)
642 0 : return AL_SUCCESS;
643 :
644 : /* check global alarm flag */
645 0 : flag = TRUE;
646 0 : size = sizeof(flag);
647 0 : db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL, TRUE);
648 0 : if (!flag)
649 0 : return AL_SUCCESS;
650 :
651 : /* request semaphore */
652 0 : cm_get_experiment_semaphore(&semaphore, NULL, NULL, NULL);
653 0 : status = ss_semaphore_wait_for(semaphore, 100);
654 0 : if (status == SS_TIMEOUT)
655 0 : return AL_SUCCESS; /* someone else is doing alarm business */
656 0 : if (status != SS_SUCCESS) {
657 0 : printf("al_check: Something is wrong with our semaphore, ss_semaphore_wait_for() returned %d, aborting.\n",
658 : status);
659 : // abort(); // DOES NOT RETURN
660 0 : printf("al_check: Cannot abort - this will lock you out of odb. From this point, MIDAS will not work "
661 : "correctly. Please read the discussion at https://midas.triumf.ca/elog/Midas/945\n");
662 : // NOT REACHED
663 0 : return AL_SUCCESS;
664 : }
665 :
666 : /* check ODB alarms */
667 0 : db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
668 0 : if (!hkeyroot) {
669 : /* create default ODB alarm */
670 0 : status = db_create_record(hDB, 0, "/Alarms/Alarms/Demo ODB", strcomb1(alarm_odb_str).c_str());
671 0 : db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
672 0 : if (!hkeyroot) {
673 0 : ss_semaphore_release(semaphore);
674 0 : return AL_SUCCESS;
675 : }
676 :
677 0 : status = db_create_record(hDB, 0, "/Alarms/Alarms/Demo periodic", strcomb1(alarm_periodic_str).c_str());
678 0 : db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
679 0 : if (!hkeyroot) {
680 0 : ss_semaphore_release(semaphore);
681 0 : return AL_SUCCESS;
682 : }
683 :
684 : /* create default alarm classes */
685 0 : status = db_create_record(hDB, 0, "/Alarms/Classes/Alarm", strcomb1(alarm_class_str).c_str());
686 0 : status = db_create_record(hDB, 0, "/Alarms/Classes/Warning", strcomb1(alarm_class_str).c_str());
687 0 : if (status != DB_SUCCESS) {
688 0 : ss_semaphore_release(semaphore);
689 0 : return AL_SUCCESS;
690 : }
691 : }
692 :
693 0 : for (int i = 0;; i++) {
694 : ALARM a;
695 :
696 0 : status = db_enum_key(hDB, hkeyroot, i, &hkey);
697 0 : if (status == DB_NO_MORE_SUBKEYS)
698 0 : break;
699 :
700 0 : db_get_key(hDB, hkey, &key);
701 :
702 0 : size = sizeof(a);
703 0 : status = db_get_record1(hDB, hkey, &a, &size, 0, strcomb1(alarm_odb_str).c_str());
704 0 : if (status != DB_SUCCESS || a.type < 1 || a.type > AT_LAST) {
705 : /* make sure alarm record has right structure */
706 0 : db_check_record(hDB, hkey, "", strcomb1(alarm_odb_str).c_str(), TRUE);
707 0 : size = sizeof(a);
708 0 : status = db_get_record1(hDB, hkey, &a, &size, 0, strcomb1(alarm_odb_str).c_str());
709 0 : if (status != DB_SUCCESS || a.type < 1 || a.type > AT_LAST) {
710 0 : cm_msg(MERROR, "al_check", "Cannot get alarm record");
711 0 : continue;
712 : }
713 : }
714 :
715 : /* check periodic alarm only when active */
716 0 : if (a.active && a.type == AT_PERIODIC && a.check_interval > 0 && (INT) ss_time() - (INT) a.checked_last > a.check_interval) {
717 : /* if checked_last has not been set, set it to current time */
718 0 : if (a.checked_last == 0) {
719 0 : a.checked_last = ss_time();
720 0 : db_set_record(hDB, hkey, &a, size, 0);
721 : } else
722 0 : al_trigger_alarm(key.name, a.alarm_message, a.alarm_class, "", AT_PERIODIC);
723 : }
724 :
725 : /* check alarm only when active and not internal */
726 0 : if (a.active && a.type == AT_EVALUATED && a.check_interval > 0 && (INT) ss_time() - (INT) a.checked_last > a.check_interval) {
727 : /* if condition is true, trigger alarm */
728 0 : std::string value;
729 0 : if (al_evaluate_condition(key.name, a.condition, &value)) {
730 0 : a.checked_last = ss_time();
731 0 : status = db_set_value(hDB, hkey, "Checked last", &a.checked_last, sizeof(DWORD), 1, TID_DWORD);
732 0 : if (status != DB_SUCCESS) {
733 0 : cm_msg(MERROR, "al_check", "Cannot change alarm record");
734 0 : continue;
735 : }
736 0 : a.trigger_count++;
737 0 : status = db_set_value(hDB, hkey, "Trigger count", &a.trigger_count, sizeof(DWORD), 1, TID_DWORD);
738 0 : if (status != DB_SUCCESS) {
739 0 : cm_msg(MERROR, "al_check", "Cannot change alarm record");
740 0 : continue;
741 : }
742 0 : if (a.trigger_count >= a.trigger_count_required) {
743 0 : a.trigger_count = 0;
744 0 : status = db_set_value(hDB, hkey, "Trigger count", &a.trigger_count, sizeof(DWORD), 1, TID_DWORD);
745 0 : if (status != DB_SUCCESS) {
746 0 : cm_msg(MERROR, "al_check", "Cannot change alarm record");
747 0 : continue;
748 : }
749 :
750 0 : std::string str = msprintf(a.alarm_message, value.c_str());
751 0 : if (a.trigger_count_required > 0)
752 0 : str += " (for more than " + std::to_string(a.trigger_count_required) + "*" +
753 0 : std::to_string(a.check_interval) + " seconds)";
754 0 : al_trigger_alarm(key.name, str.c_str(), a.alarm_class, "", AT_EVALUATED);
755 0 : }
756 : } else {
757 0 : a.checked_last = ss_time();
758 0 : status = db_set_value(hDB, hkey, "Checked last", &a.checked_last, sizeof(DWORD), 1, TID_DWORD);
759 0 : if (status != DB_SUCCESS) {
760 0 : cm_msg(MERROR, "al_check", "Cannot change alarm record");
761 0 : continue;
762 : }
763 0 : a.trigger_count = 0;
764 0 : status = db_set_value(hDB, hkey, "Trigger count", &a.trigger_count, sizeof(DWORD), 1, TID_DWORD);
765 0 : if (status != DB_SUCCESS) {
766 0 : cm_msg(MERROR, "al_check", "Cannot change alarm record");
767 0 : continue;
768 : }
769 : }
770 0 : }
771 0 : }
772 :
773 : /* check /programs alarms */
774 0 : db_find_key(hDB, 0, "/Programs", &hkeyroot);
775 0 : if (hkeyroot) {
776 0 : for (int i = 0;; i++) {
777 0 : status = db_enum_key(hDB, hkeyroot, i, &hkey);
778 0 : if (status == DB_NO_MORE_SUBKEYS)
779 0 : break;
780 :
781 0 : db_get_key(hDB, hkey, &key);
782 :
783 : /* don't check "execute on xxx" */
784 0 : if (key.type != TID_KEY)
785 0 : continue;
786 :
787 0 : size = sizeof(program_info);
788 0 : status = db_get_record1(hDB, hkey, &program_info, &size, 0, strcomb1(program_info_str).c_str());
789 0 : if (status != DB_SUCCESS) {
790 0 : cm_msg(MERROR, "al_check",
791 : "Cannot get program info record for program \"%s\", db_get_record1() status %d", key.name,
792 : status);
793 0 : continue;
794 : }
795 :
796 0 : time_t now = ss_time();
797 :
798 : // get name of this client
799 0 : std::string name = rpc_get_name();
800 0 : std::string str = name;
801 :
802 : // truncate name of this client to the length of program name in /Programs/xxx
803 : // to get rid if "odbedit1", "odbedit2", etc.
804 0 : str.resize(strlen(key.name));
805 :
806 : //printf("str [%s], key.name [%s]\n", str.c_str(), key.name);
807 :
808 0 : if (!equal_ustring(str.c_str(), key.name) && cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
809 0 : if (program_info.first_failed == 0) {
810 0 : program_info.first_failed = (DWORD) now;
811 0 : db_set_record(hDB, hkey, &program_info, sizeof(program_info), 0);
812 : }
813 :
814 : // printf("check %d-%d = %d >= %d\n", now, program_info.first_failed, now - program_info.first_failed,
815 : // program_info.check_interval / 1000);
816 :
817 : /* fire alarm when not running for more than what specified in check interval */
818 0 : if (now - program_info.first_failed >= program_info.check_interval / 1000) {
819 : /* if not running and alarm class defined, trigger alarm */
820 0 : if (program_info.alarm_class[0]) {
821 0 : std::string str = msprintf("Program %s is not running", key.name);
822 0 : al_trigger_alarm(key.name, str.c_str(), program_info.alarm_class, "Program not running", AT_PROGRAM);
823 0 : }
824 :
825 : /* auto restart program */
826 0 : if (program_info.auto_restart && program_info.start_command[0]) {
827 0 : ss_system(program_info.start_command);
828 0 : program_info.first_failed = 0;
829 0 : cm_msg(MTALK, "al_check", "Program %s restarted", key.name);
830 : }
831 : }
832 : } else {
833 0 : if (program_info.first_failed != 0) {
834 0 : program_info.first_failed = 0;
835 0 : db_set_record(hDB, hkey, &program_info, sizeof(program_info), 0);
836 : }
837 : }
838 0 : }
839 : }
840 :
841 0 : ss_semaphore_release(semaphore);
842 : }
843 : #endif /* LOCAL_COUTINES */
844 :
845 0 : return SUCCESS;
846 : }
847 :
848 : /********************************************************************/
849 : /**
850 : Scan ODB for alarms.
851 : @return AL_SUCCESS
852 : */
853 0 : INT al_get_alarms(std::string *presult)
854 : {
855 0 : if (!presult)
856 0 : return 0;
857 :
858 : HNDLE hDB, hkey;
859 :
860 0 : cm_get_experiment_database(&hDB, NULL);
861 0 : presult->clear();
862 0 : int n = 0;
863 0 : db_find_key(hDB, 0, "/Alarms/Alarms", &hkey);
864 0 : if (hkey) {
865 : /* check global alarm flag */
866 0 : int flag = TRUE;
867 0 : int size = sizeof(flag);
868 0 : db_get_value(hDB, 0, "/Alarms/Alarm System active", &flag, &size, TID_BOOL, FALSE);
869 0 : if (flag) {
870 0 : for (int i = 0;; i++) {
871 : HNDLE hsubkey;
872 :
873 0 : db_enum_link(hDB, hkey, i, &hsubkey);
874 :
875 0 : if (!hsubkey)
876 0 : break;
877 :
878 : KEY key;
879 0 : db_get_key(hDB, hsubkey, &key);
880 :
881 0 : flag = 0;
882 0 : size = sizeof(flag);
883 0 : db_get_value(hDB, hsubkey, "Triggered", &flag, &size, TID_INT, FALSE);
884 0 : if (flag) {
885 0 : n++;
886 :
887 0 : std::string alarm_class, msg;
888 : int alarm_type;
889 :
890 0 : db_get_value_string(hDB, hsubkey, "Alarm Class", 0, &alarm_class);
891 0 : db_get_value_string(hDB, hsubkey, "Alarm Message", 0, &msg);
892 :
893 0 : size = sizeof(alarm_type);
894 0 : db_get_value(hDB, hsubkey, "Type", &alarm_type, &size, TID_INT, FALSE);
895 :
896 0 : std::string str;
897 :
898 0 : if (alarm_type == AT_EVALUATED) {
899 0 : db_get_value_string(hDB, hsubkey, "Condition", 0, &str);
900 :
901 : /* retrieve value */
902 0 : std::string value;
903 0 : al_evaluate_condition(key.name, str.c_str(), &value);
904 0 : str = msprintf(msg.c_str(), value.c_str());
905 0 : } else
906 0 : str = msg;
907 :
908 0 : *presult += alarm_class;
909 0 : *presult += ": ";
910 0 : *presult += str;
911 0 : *presult += "\n";
912 0 : }
913 0 : }
914 : }
915 : }
916 :
917 0 : return n;
918 : }
919 :
920 : /********************************************************************/
921 : /**
922 : Create an alarm that will trigger when an ODB condition is met.
923 :
924 : @param name Alarm name, defined in /alarms/alarms
925 : @param class Alarm class to be triggered
926 : @param condition Alarm condition to be evaluated
927 : @param message Alarm message
928 : @return AL_SUCCESS
929 : */
930 0 : INT EXPRT al_define_odb_alarm(const char *name, const char *condition, const char *aclass, const char *message) {
931 : HNDLE hDB, hKey;
932 0 : ALARM_ODB_STR(alarm_odb_str);
933 :
934 0 : cm_get_experiment_database(&hDB, nullptr);
935 :
936 0 : std::string str = msprintf("/Alarms/Alarms/%s", name);
937 :
938 0 : db_create_record(hDB, 0, str.c_str(), strcomb1(alarm_odb_str).c_str());
939 0 : db_find_key(hDB, 0, str.c_str(), &hKey);
940 0 : if (!hKey)
941 0 : return DB_NO_MEMORY;
942 :
943 0 : db_set_value(hDB, hKey, "Condition", condition, 256, 1, TID_STRING);
944 0 : db_set_value(hDB, hKey, "Alarm Class", aclass, 32, 1, TID_STRING);
945 0 : db_set_value(hDB, hKey, "Alarm Message", message, 80, 1, TID_STRING);
946 :
947 0 : return AL_SUCCESS;
948 0 : }
949 :
950 : /**dox***************************************************************/
951 : /** @} */ /* end of alfunctioncode */
952 :
953 : /* emacs
954 : * Local Variables:
955 : * tab-width: 8
956 : * c-basic-offset: 3
957 : * indent-tabs-mode: nil
958 : * End:
959 : */
|