32#ifndef DOXYGEN_SHOULD_SKIP_THIS
48 std::vector<char> buf;
49 buf.insert(buf.end(), condition, condition+strlen(condition)+1);
50 char*
str = buf.data();
54 op[0] = op[1] = op[2] = 0;
55 for (
i = strlen(
str) - 1;
i > 0;
i--)
56 if (strchr(
"<>=!&",
str[
i]) != NULL)
59 for (
j = 1;
str[
i +
j] ==
' ';
j++)
62 std::string value2_str =
str +
i +
j;
63 double value2 = atof(value2_str.c_str());
67 if (
i > 0 && strchr(
"<>=!&",
str[
i - 1])) {
74 while (
i > 0 &&
str[
i] ==
' ')
82 if (strchr(
str,
'(')) {
83 *strchr(
str,
'(') = 0;
92 std::vector<int> indices;
93 bool index_present =
false;
96 if (strchr(
str,
'[') &&
str[strlen(
str)-1] ==
']') {
100 std::string cleanInput = strchr(
str,
'[') + 1;
101 cleanInput.pop_back();
104 *strchr(
str,
'[') = 0;
108 cm_msg(
MERROR,
"al_evaluate_condition",
"Alarm \"%s\": Cannot find ODB key \"%s\" to evaluate alarm condition", alarm_name,
str);
110 *pvalue =
"(cannot find in odb)";
116 std::stringstream ss(cleanInput);
118 while (std::getline(ss, token,
',')) {
120 token.erase(std::remove_if(token.begin(), token.end(), ::isspace), token.end());
124 for (
int i = 0;
i <
k.get_num_values(); ++
i)
125 indices.push_back(
i);
128 auto dashPos = token.find(
'-');
129 if (dashPos != std::string::npos) {
131 int start = std::stoi(token.substr(0, dashPos));
132 int end = std::stoi(token.substr(dashPos + 1));
135 for (
int i = start;
i <=
end; ++
i) {
136 indices.push_back(
i);
140 indices.push_back(std::stoi(token));
150 cm_msg(
MERROR,
"al_evaluate_condition",
"Alarm \"%s\": Cannot find ODB key \"%s\" to evaluate alarm condition", alarm_name,
str);
152 *pvalue =
"(cannot find in odb)";
163 indices.push_back(0);
166 for (
int idx : indices) {
167 std::string value1_str;
175 value1 = atof(value1_str.c_str());
176 }
else if (
equal_ustring(function.c_str(),
"access_running")) {
181 size =
sizeof(
state);
185 value1 = atof(value1_str.c_str());
194 cm_msg(
MERROR,
"al_evaluate_condition",
"Alarm \"%s\": Cannot get ODB key \"%s\" to evaluate alarm condition", alarm_name,
str);
196 *pvalue =
"(odb error)";
203 cm_msg(
MERROR,
"al_evaluate_condition",
"Alarm \"%s\": Cannot get ODB value \"%s\" index %d to evaluate alarm condition", alarm_name,
str, idx);
205 *pvalue =
"(odb error)";
210 value1 = atof(value1_str.c_str());
216 value1 = (value1_str[0] ==
'Y' || value1_str[0] ==
'y' || value1_str[0] ==
'1');
217 value2 = (value2_str[0] ==
'Y' || value2_str[0] ==
'y' || value2_str[0] ==
'1');
223 *pvalue =
msprintf(
"[%d] %s", idx, value1_str.c_str());
225 *pvalue = value1_str;
229 if (strcmp(op,
"=") == 0)
230 if (value1 == value2)
232 if (strcmp(op,
"==") == 0)
233 if (value1 == value2)
235 if (strcmp(op,
"!=") == 0)
236 if (value1 != value2)
238 if (strcmp(op,
"<") == 0)
241 if (strcmp(op,
">") == 0)
244 if (strcmp(op,
"<=") == 0)
245 if (value1 <= value2)
247 if (strcmp(op,
">=") == 0)
248 if (value1 >= value2)
250 if (strcmp(op,
"&") == 0)
251 if (((
unsigned int) value1 & (
unsigned int) value2) > 0)
261 auto trim = [](
const std::string& s) {
262 size_t first = s.find_first_not_of(
" \t\r\n");
263 if (first == std::string::npos)
264 return std::string();
265 size_t last = s.find_last_not_of(
" \t\r\n");
266 return s.substr(first, last - first + 1);
269 auto invalid_condition = [alarm_name, pvalue](
const char* condition) {
270 cm_msg(
MERROR,
"al_evaluate_condition",
"Alarm \"%s\": Invalid alarm condition \"%s\"",
271 alarm_name, condition ? condition :
"");
273 *pvalue =
"(invalid condition)";
277 if (condition ==
nullptr || trim(condition).empty())
278 return invalid_condition(condition);
280 std::string expression(condition);
281 std::vector<std::vector<std::string>> groups;
287 while (or_begin <= expression.size()) {
288 size_t or_end = expression.find(
"||", or_begin);
289 std::string group = expression.substr(or_begin, or_end - or_begin);
290 if (trim(group).empty())
291 return invalid_condition(condition);
293 std::vector<std::string> terms;
294 size_t and_begin = 0;
295 while (and_begin <= group.size()) {
296 size_t and_end = group.find(
"&&", and_begin);
297 std::string
term = trim(group.substr(and_begin, and_end - and_begin));
299 return invalid_condition(condition);
300 terms.push_back(
term);
301 if (and_end == std::string::npos)
303 and_begin = and_end + 2;
305 groups.push_back(terms);
307 if (or_end == std::string::npos)
309 or_begin = or_end + 2;
312 std::string all_values;
313 bool expression_result =
false;
317 for (
const auto& group : groups) {
318 bool group_result =
true;
319 for (
const auto&
term : group) {
320 std::string term_value;
323 group_result =
false;
324 if (!all_values.empty())
326 all_values += term_value;
329 expression_result =
true;
333 *pvalue = all_values;
335 return expression_result;
344 std::string time_triggered(31,
'\0');
345 std::string condition(255,
'\0');
346 std::string alarm_class = periodic ?
"Warning" :
"Alarm";
347 std::string alarm_message = periodic ?
"Please do your shift checks" :
"Run number became too large";
348 alarm_class.resize(31,
'\0');
349 alarm_message.resize(79,
'\0');
355 {
"Check interval", periodic ? 28800 : 60},
356 {
"Checked last", (uint32_t)0},
357 {
"Trigger count", (uint32_t)0},
358 {
"Trigger count required", (uint32_t)0},
359 {
"Time triggered first", time_triggered},
360 {
"Time triggered last", time_triggered},
361 {
"Condition", condition},
362 {
"Alarm Class", alarm_class},
363 {
"Alarm Message", alarm_message}
367 condition.replace(0, strlen(
"/Runinfo/Run number > 100"),
"/Runinfo/Run number > 100");
368 alarm[
"Condition"].
set(condition);
396INT al_trigger_alarm(
const char *alarm_name,
const char *alarm_message,
const char *default_class,
const char *cond_str,
417 std::string alarm_path =
msprintf(
"/Alarms/Alarms/%s", alarm_name);
424 if (default_class && default_class[0])
425 alarm[
"Alarm Class"] = default_class;
426 alarm[
"Active"] =
true;
431 alarm[
"Type"] =
type;
432 alarm[
"Condition"] = cond_str;
435 int alarm_type = alarm[
"Type"];
446 if (!(
bool)alarm[
"Active"])
449 DWORD checked_last = alarm[
"Checked last"];
450 int check_interval = alarm[
"Check interval"];
455 std::string alarm_class = alarm[
"Alarm Class"];
461 "Program alarm class mismatch: \"%s/Alarm class\" set to \"%s\" does not match \"/Programs/%s/Alarm "
462 "Class\" set to \"%s\"",
463 alarm_path.c_str(), alarm_class.c_str(), alarm_name, default_class);
469 alarm[
"Alarm Message"] = alarm_message;
472 int triggered = alarm[
"Triggered"];
473 if (!alarm_class.empty())
484 alarm[
"Time triggered first"] = now;
486 alarm[
"Triggered"] = ++triggered;
487 alarm[
"Time triggered last"] = now;
488 alarm[
"Checked last"] = (uint32_t)
ss_time();
496#ifndef DOXYGEN_SHOULD_SKIP_THIS
528 std::string alarm_path =
msprintf(
"/Alarms/Classes/%s", alarm_class);
530 cm_msg(
MERROR,
"al_trigger_class",
"Alarm class \"%s\" for alarm \"%s\" not found in ODB", alarm_class, alarm_message);
539 bool write_system_message = ac[
"Write system message"];
540 int system_message_interval = ac[
"System message interval"];
541 DWORD system_message_last = ac[
"System message last"];
542 if (write_system_message && (now - system_message_last >= (
DWORD) system_message_interval)) {
544 msg =
msprintf(
"General alarm: %s", alarm_message);
546 msg =
msprintf(
"%s: %s", alarm_class, alarm_message);
547 cm_msg(
MTALK,
"al_trigger_class",
"%s", msg.c_str());
548 system_message_last = now;
549 ac[
"System message last"] = (uint32_t)system_message_last;
553 if ((
bool)ac[
"Write Elog message"] && first) {
555 size =
sizeof(external_elog);
557 if (!external_elog) {
560 el_submit(0,
"Alarm system",
"Alarm",
"General", alarm_class, msg.c_str(),
"",
"plain",
"",
"", 0,
"",
"", 0,
"",
"", 0, tag,
sizeof(tag));
565 std::string execute_command = ac[
"Execute command"];
566 int execute_interval = ac[
"Execute interval"];
567 DWORD execute_last = ac[
"Execute last"];
568 if (!execute_command.empty() && execute_interval > 0 && (
INT)
ss_time() - (
INT) execute_last > execute_interval) {
570 msg =
msprintf(
"General alarm: %s", alarm_message);
572 msg =
msprintf(
"%s: %s", alarm_class, alarm_message);
573 std::string command =
msprintf(execute_command.c_str(), msg.c_str());
574 cm_msg(
MINFO,
"al_trigger_class",
"Execute: %s", command.c_str());
577 ac[
"Execute last"] = (uint32_t)execute_last;
581 if ((
bool)ac[
"Stop run"]) {
583 size =
sizeof(
state);
586 cm_msg(
MINFO,
"al_trigger_class",
"Stopping the run from alarm class \'%s\', message \'%s\'", alarm_class,
612 if (alarm_name == NULL) {
630 std::string
str =
msprintf(
"/Alarms/Alarms/%s", alarm_name);
640 std::string alarm_class = alarm[
"Alarm Class"];
641 str =
msprintf(
"/Alarms/Classes/%s", alarm_class.c_str());
643 cm_msg(
MERROR,
"al_reset_alarm",
"Alarm class %s not found in ODB", alarm_class.c_str());
649 int triggered = alarm[
"Triggered"];
651 int alarm_type = alarm[
"Type"];
657 memset(&tm_time, 0,
sizeof(tm_time));
659 std::string time_triggered_first = alarm[
"Time triggered first"];
660 strptime(time_triggered_first.c_str(),
"%a %b %d %H:%M:%S %Y", &tm_time);
661 time_t t = mktime(&tm_time);
663 int check_interval = alarm[
"Check interval"];
664 while ((
int)
ss_time() > t + check_interval)
667 alarm[
"Checked last"] = (uint32_t)t;
669 alarm[
"Checked last"] = (uint32_t)0;
672 alarm[
"Triggered"] = 0;
673 alarm[
"Time triggered first"] =
"";
674 alarm[
"Time triggered last"] =
"";
675 alarm[
"Trigger count"] = (uint32_t)0;
677 ac[
"System message last"] = (uint32_t)0;
678 ac[
"Execute last"] = (uint32_t)0;
680 cm_msg(
MINFO,
"al_reset_alarm",
"Alarm \"%s\" reset", alarm_name);
730 fprintf(stderr,
"al_check: Something is wrong with our semaphore, ss_semaphore_wait_for() returned %d, aborting.\n",
status);
733 fprintf(stderr,
"al_check: Cannot abort - this will lock you out of odb. From this point, MIDAS will not work correctly. Please read the discussion at https://midas.triumf.ca/elog/Midas/945\n");
742 demo_odb.
connect(
"/Alarms/Alarms/Demo ODB");
751 demo_periodic.
connect(
"/Alarms/Alarms/Demo periodic");
761 {
"Write system message",
true},
762 {
"Write Elog message",
false},
763 {
"System message interval", 60},
764 {
"System message last", (uint32_t)0},
765 {
"Execute command",
""},
766 {
"Execute interval", 0},
767 {
"Execute last", (uint32_t)0},
769 {
"Display BGColor",
"red"},
770 {
"Display FGColor",
"black"},
771 {
"Alarm sound",
true}
773 auto warning = alarm;
774 alarm.
connect(
"/Alarms/Classes/Alarm");
775 warning[
"Display BGColor"] =
"yellow";
776 warning.connect(
"/Alarms/Classes/Warning");
779 for (
int i = 0;;
i++) {
786 std::string alarm_path =
"/Alarms/Alarms/" + std::string(
key.
name);
790 int alarm_type = alarm[
"Type"];
791 if (alarm_type < 1 || alarm_type >
AT_LAST) {
796 bool active = alarm[
"Active"];
797 int check_interval = alarm[
"Check interval"];
798 DWORD checked_last = alarm[
"Checked last"];
801 if (active && alarm_type ==
AT_PERIODIC && check_interval > 0 && (
INT)
ss_time() - (
INT) checked_last > check_interval) {
803 if (checked_last == 0) {
805 alarm[
"Checked last"] = checked_last;
807 std::string alarm_message = alarm[
"Alarm Message"];
808 std::string alarm_class = alarm[
"Alarm Class"];
814 if (active && alarm_type ==
AT_EVALUATED && check_interval > 0 && (
INT)
ss_time() - (
INT) checked_last > check_interval) {
816 std::string condition = alarm[
"Condition"];
819 alarm[
"Checked last"] = (uint32_t)
ss_time();
820 DWORD trigger_count = alarm[
"Trigger count"];
821 DWORD trigger_count_required = alarm[
"Trigger count required"];
822 alarm[
"Trigger count"] = ++trigger_count;
823 if (trigger_count >= trigger_count_required) {
824 alarm[
"Trigger count"] = (uint32_t)0;
826 std::string
str = alarm[
"Alarm Message"];
827 size_t value_pos =
str.find(
"%s");
828 if (value_pos != std::string::npos)
830 if (trigger_count_required > 0)
831 str +=
" (for more than " + std::to_string(trigger_count_required) +
"*" +
832 std::to_string(check_interval) +
" seconds)";
833 std::string alarm_class = alarm[
"Alarm Class"];
837 alarm[
"Checked last"] = (uint32_t)
ss_time();
838 alarm[
"Trigger count"] = (uint32_t)0;
846 for (
int i = 0;;
i++) {
876 DWORD first_failed = program_info[
"First failed"];
877 if (first_failed == 0) {
878 first_failed = (
DWORD) now;
879 program_info[
"First failed"] = first_failed;
886 DWORD check_interval = program_info[
"Check interval"];
887 if (now - first_failed >= check_interval / 1000) {
889 std::string alarm_class = program_info[
"Alarm class"];
890 if (!alarm_class.empty()) {
897 std::string start_command = program_info[
"Start command"];
900 program_info[
"First failed"] = (uint32_t)0;
905 DWORD first_failed = program_info[
"First failed"];
906 if (first_failed != 0)
907 program_info[
"First failed"] = (uint32_t)0;
938 int size =
sizeof(flag);
941 for (
int i = 0;;
i++) {
958 std::string alarm_class, msg;
964 size =
sizeof(alarm_type);
979 *presult += alarm_class;
1006 alarm[
"Condition"] = condition;
1007 alarm[
"Alarm Class"] = aclass;
1008 alarm[
"Alarm Message"] =
message;
void set(std::string str)
static bool exists(const std::string &name)
void connect_and_fix_structure(std::string path)
void connect(const std::string &path, const std::string &name, bool write_defaults, bool delete_keys_not_in_defaults=false)
static BOOL al_evaluate_single_condition(const char *alarm_name, const char *condition, std::string *pvalue)
INT al_trigger_class(const char *alarm_class, const char *alarm_message, BOOL first)
static midas::odb al_make_alarm_odb(bool periodic=false)
BOOL al_evaluate_condition(const char *alarm_name, const char *condition, std::string *pvalue)
INT EXPRT al_define_odb_alarm(const char *name, const char *condition, const char *aclass, const char *message)
INT al_get_alarms(std::string *presult)
INT al_reset_alarm(const char *alarm_name)
INT al_trigger_alarm(const char *alarm_name, const char *alarm_message, const char *default_class, const char *cond_str, INT type)
INT cm_get_experiment_database(HNDLE *hDB, HNDLE *hKeyClient)
INT cm_get_experiment_semaphore(INT *semaphore_alarm, INT *semaphore_elog, INT *semaphore_history, INT *semaphore_msg)
INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
INT cm_exist(const char *name, BOOL bClientName)
INT el_submit(int run, const char *author, const char *type, const char *syst, const char *subject, const char *text, const char *reply_to, const char *encoding, const char *afilename1, const char *buffer1, INT buffer_size1, const char *afilename2, const char *buffer2, INT buffer_size2, const char *afilename3, const char *buffer3, INT buffer_size3, char *tag, INT tag_size)
#define DB_NO_MORE_SUBKEYS
INT ss_semaphore_release(HNDLE semaphore_handle)
INT ss_semaphore_wait_for(HNDLE semaphore_handle, DWORD timeout_millisec)
INT ss_system(const char *command)
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
BOOL equal_ustring(const char *str1, const char *str2)
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT *buf_size, DWORD type, BOOL create)
INT db_get_key(HNDLE hDB, HNDLE hKey, KEY *key)
INT EXPRT db_get_value_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int index, std::string *s, BOOL create, int create_string_length)
INT db_enum_link(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
INT db_get_key_time(HNDLE hDB, HNDLE hKey, DWORD *delta)
INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
std::string rpc_get_name()
#define RPC_AL_TRIGGER_ALARM
INT rpc_call(DWORD routine_id,...)
std::string msprintf(const char *format,...)
#define message(type, str)
static te_expr * term(state *s)