LCOV - code coverage report
Current view: top level - src - alarm.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 497 0
Test Date: 2025-11-11 10:26:08 Functions: 0.0 % 7 0

            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              :  */
        

Generated by: LCOV version 2.0-1