]> Kevux Git Server - fll/commitdiff
Progress: controller program.
authorKevin Day <thekevinday@gmail.com>
Wed, 9 Dec 2020 04:39:15 +0000 (22:39 -0600)
committerKevin Day <thekevinday@gmail.com>
Wed, 9 Dec 2020 04:39:15 +0000 (22:39 -0600)
level_3/controller/c/private-common.h
level_3/controller/c/private-controller.c
level_3/controller/c/private-rule.c
level_3/controller/c/private-rule.h

index 7e6e906ff8ddaa04d11916284bd587fa8c7f502c..342e233c9c4dd6964a83e68bcb54d974faf5e379 100644 (file)
@@ -223,6 +223,7 @@ extern "C" {
 
   typedef struct {
     f_status_t status;
+    f_number_signed_t process; // @todo: for background/threaded support (ideally should hold the process id, but remove if this ends up not being the strategy) (this can also be used by the parent/main process to check to see if the child no longer a child of this process).
 
     f_time_spec_t timestamp;
 
@@ -246,6 +247,7 @@ extern "C" {
     { \
       F_known_not, \
       0, \
+      0, \
       f_time_spec_t_initialize, \
       f_string_dynamic_t_initialize, \
       f_string_dynamic_t_initialize, \
@@ -441,6 +443,7 @@ extern "C" {
 
   typedef struct {
     bool interruptable;
+    bool lock; // @todo: this is intend for mutex write locking of this setting for thread safety, remove this if another approach is used.
     uint8_t ready;
 
     f_string_dynamic_t path_pid;
@@ -453,6 +456,7 @@ extern "C" {
   #define controller_setting_t_initialize \
     { \
       F_false, \
+      F_false, \
       0, \
       f_string_dynamic_t_initialize, \
       f_string_dynamic_t_initialize, \
@@ -477,6 +481,7 @@ extern "C" {
     f_string_range_t range_action;
 
     f_array_lengths_t ats;
+    f_array_lengths_t stack;
 
     f_fss_comments_t comments;
     f_fss_delimits_t delimits;
@@ -503,6 +508,7 @@ extern "C" {
       f_time_spec_t_initialize, \
       f_string_range_t_initialize, \
       f_array_lengths_t_initialize, \
+      f_array_lengths_t_initialize, \
       f_fss_comments_t_initialize, \
       f_fss_delimits_t_initialize, \
       f_fss_content_t_initialize, \
@@ -520,6 +526,7 @@ extern "C" {
 
   #define controller_macro_cache_t_delete_simple(cache) \
     f_macro_array_lengths_t_delete_simple(cache.ats) \
+    f_macro_array_lengths_t_delete_simple(cache.stack) \
     f_macro_fss_comments_t_delete_simple(cache.comments) \
     f_macro_fss_delimits_t_delete_simple(cache.delimits) \
     f_macro_fss_content_t_delete_simple(cache.content_action) \
index 7b9c526f0d572be31c2bba94aa1b5f6a2b3f9529..057ebf9d5fb58c6826565ab52c30014acd744b42 100644 (file)
@@ -525,6 +525,7 @@ extern "C" {
     cache->line_item = 0;
     cache->name_action.used = 0;
     cache->name_item.used = 0;
+    cache->stack.used = 0;
 
     if (setting->ready == controller_setting_ready_yes) {
       status = controller_perform_ready(data, setting, cache);
@@ -645,20 +646,6 @@ extern "C" {
             return status;
           }
           else {
-
-            // rule execution will re-use the existing cache, so save the current cache.
-            const f_array_length_t cache_line_action = cache->line_action;
-            const f_array_length_t cache_line_item = cache->line_item;
-
-            const f_string_length_t cache_name_action_used = cache->name_action.used;
-            const f_string_length_t cache_name_item_used = cache->name_item.used;
-
-            char cache_name_item[cache_name_action_used];
-            char cache_name_action[cache_name_item_used];
-
-            memcpy(cache_name_item, cache->name_item.string, cache->name_item.used);
-            memcpy(cache_name_action, cache->name_action.string, cache->name_action.used);
-
             const f_string_length_t rule_id_length = actions->array[cache->ats.array[at_j]].parameters.array[0].used + actions->array[cache->ats.array[at_j]].parameters.array[1].used + 1;
             char rule_id_name[rule_id_length + 1];
             const f_string_static_t rule_id = f_macro_string_static_t_initialize(rule_id_name, rule_id_length);
@@ -684,40 +671,60 @@ extern "C" {
               }
             }
 
-            if (F_status_is_error_not(status) && actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
-              if (similate) {
-                // @todo print message about how this would execute the rule.
-              }
-              else {
-                //status = controller_rule_process();
+            if (F_status_is_error_not(status)) {
+
+              // rule execution will re-use the existing cache, so save the current cache.
+              const f_array_length_t cache_line_action = cache->line_action;
+              const f_array_length_t cache_line_item = cache->line_item;
+
+              const f_string_length_t cache_name_action_used = cache->name_action.used;
+              const f_string_length_t cache_name_item_used = cache->name_item.used;
+              const f_string_length_t cache_name_file_used = cache->name_file.used;
+
+              char cache_name_action[cache_name_action_used];
+              char cache_name_item[cache_name_item_used];
+              char cache_name_file[cache_name_file_used];
+
+              memcpy(cache_name_action, cache->name_action.string, cache->name_action.used);
+              memcpy(cache_name_item, cache->name_item.string, cache->name_item.used);
+              memcpy(cache_name_file, cache->name_file.string, cache->name_file.used);
+
+              if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
+                // @todo: this will also need to support the asynchronous/wait behavior.
+                status = controller_rule_process(data, at, simulate, setting, cache);
               }
-            }
 
-            // restore cache.
-            memcpy(cache->name_action.string, cache_name_action, cache_name_action_used);
-            memcpy(cache->name_item.string, cache_name_item, cache_name_item_used);
+              // restore cache.
+              memcpy(cache->name_action.string, cache_name_action, cache_name_action_used);
+              memcpy(cache->name_item.string, cache_name_item, cache_name_item_used);
+              memcpy(cache->name_file.string, cache_name_file, cache_name_file_used);
 
-            cache->name_action.string[cache_name_action_used] = 0;
-            cache->name_item.string[cache_name_item_used] = 0;
+              cache->name_action.string[cache_name_action_used] = 0;
+              cache->name_item.string[cache_name_item_used] = 0;
+              cache->name_file.string[cache_name_file_used] = 0;
 
-            cache->name_action.used = cache_name_action_used;
-            cache->name_item.used = cache_name_item_used;
+              cache->name_action.used = cache_name_action_used;
+              cache->name_item.used = cache_name_item_used;
+              cache->name_file.used = cache_name_file_used;
 
-            cache->line_action = cache_line_action;
-            cache->line_item = cache_line_item;
+              cache->line_action = cache_line_action;
+              cache->line_item = cache_line_item;
+            }
 
             if (F_status_is_error(status)) {
               controller_entry_error_print(data.error, *cache);
 
-              if (!simulate) break;
+              if (!simulate || F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
+                break;
+              }
             }
           }
         }
         else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_timeout) {
-          // @todo
+          // @todo set the appropriate timeout value (set the entry actions timeouts which are later used as the initial defaults as the rule timeouts).
         }
         else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_failsafe) {
-          // @todo
+          // @todo set the failsafe rule to this rule id (find the rule and then assign by the rule id and then assign by the array index).
         }
 
       } // for
@@ -725,6 +732,12 @@ extern "C" {
       cache->line_action = 0;
       cache->name_action.used = 0;
 
+      if (F_status_is_error(status)) {
+        if (!simulate || F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
+          break;
+        }
+      }
+
       // end of actions found, so drop to previous loop in stack.
       if (cache->ats.array[at_j] == actions->used) {
 
@@ -746,7 +759,7 @@ extern "C" {
           fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
           controller_entry_error_print(data.error, *cache);
 
-          return status;
+          break;
         }
       }
     } // for
index 9b595d320d4291d7034d47421a5287e7091bada0..41a94aebad68cb8f97b024c809af9a5bdb6666ae 100644 (file)
@@ -274,7 +274,7 @@ extern "C" {
 
     for (; i < setting.rules.used; ++i) {
 
-      if (fl_string_dynamic_compare(setting.rules.array[i], rule_id) == F_equal_to) {
+      if (fl_string_dynamic_compare(setting.rules.array[i].id, rule_id) == F_equal_to) {
         return i;
       }
     } // for
@@ -644,6 +644,274 @@ extern "C" {
   }
 #endif // _di_controller_rule_read_
 
+#ifndef _di_controller_rule_process_
+  f_return_status controller_rule_process(const controller_data_t data, const f_array_length_t index, const bool simulate, controller_setting_t *setting, controller_cache_t *cache) {
+
+    if (index >= setting->rules.used) {
+      fll_error_print(data.error, F_parameter, "controller_rule_process", F_true);
+      controller_rule_error_print(data.error, *cache, F_true);
+
+      return F_status_set_error(F_parameter);
+    }
+
+    f_status_t status = fl_type_array_lengths_increase_by(controller_default_allocation_step, &cache->stack);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data.error, F_status_set_fine(status), "fl_type_array_lengths_increase_by", F_true);
+      controller_rule_error_print(data.error, *cache, F_true);
+
+      return status;
+    }
+
+    f_array_length_t i = 0;
+
+    for (; i < cache->stack.used; ++i) {
+
+      if (cache->stack.array[i] == index) {
+        if (data.error.verbosity != f_console_verbosity_quiet) {
+          fprintf(data.error.to.stream, "%c", f_string_eol[0]);
+          fprintf(data.error.to.stream, "%s%sThe rule '", data.error.context.before->string, data.error.prefix ? data.error.prefix : "");
+          fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, setting->rules.array[i].name.string, data.error.notable.after->string);
+          fprintf(data.error.to.stream, "%s' is already on the execution stack, this recursion is prohibited.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol[0]);
+
+          controller_rule_error_print(data.error, *cache, F_true);
+        }
+
+        // never continue on recursion errors even in simulate mode.
+        return F_status_set_error(F_recurse);
+      }
+    }
+
+    cache->stack.array[cache->stack.used++] = index;
+
+    cache->name_action.used = 0;
+    cache->name_item.used = 0;
+    cache->name_file.used = 0;
+
+    status = fl_string_append(controller_string_rules, controller_string_rules_length, &cache->name_file);
+
+    if (F_status_is_error_not(status)) {
+      status = fl_string_append(f_path_separator, f_path_separator_length, &cache->name_file);
+    }
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data.error, F_status_set_fine(status), "fl_string_append", F_true);
+      controller_rule_error_print(data.error, *cache, F_true);
+
+      return status;
+    }
+
+    status = fl_string_dynamic_append(setting->rules.array[index].id, &cache->name_file);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_append", F_true);
+      controller_rule_error_print(data.error, *cache, F_true);
+
+      return status;
+    }
+
+    status = fl_string_append(f_path_extension_separator, f_path_extension_separator_length, &cache->name_file);
+
+    if (F_status_is_error_not(status)) {
+      status = fl_string_append(controller_string_rule, controller_string_rule_length, &cache->name_file);
+    }
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data.error, F_status_set_fine(status), "fl_string_append", F_true);
+      controller_rule_error_print(data.error, *cache, F_true);
+
+      return status;
+    }
+
+    status = fl_string_dynamic_terminate_after(&cache->name_file);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
+      controller_rule_error_print(data.error, *cache, F_true);
+
+      return status;
+    }
+
+    controller_rule_t *rule = &setting->rules.array[index];
+
+    {
+      f_array_length_t j = 0;
+      f_array_length_t k = 0;
+      f_array_length_t at = 0;
+
+      f_string_dynamics_t *dynamics[] = {
+        &rule->need,
+        &rule->want,
+        &rule->wish,
+      };
+
+      for (i = 0; i < 3; ++i) {
+
+        for (j = 0; j < dynamics[i]->used; ++j) {
+          at = controller_rule_find_loaded(data, *setting, dynamics[i]->array[j]);
+
+          if (at == setting->rules.used) {
+            if (i == 0) {
+
+              if (data.error.verbosity != f_console_verbosity_quiet) {
+                fprintf(data.error.to.stream, "%c", f_string_eol[0]);
+                fprintf(data.error.to.stream, "%s%sThe needed rule '", data.error.context.before->string, data.error.prefix ? data.error.prefix : "");
+                fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, dynamics[i]->array[j].string, data.error.notable.after->string);
+                fprintf(data.error.to.stream, "%s' was not found.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol[0]);
+
+                controller_rule_error_print(data.error, *cache, F_true);
+              }
+
+              status = F_status_set_error(F_found_not);
+
+              if (!simulate) break;
+            }
+            else {
+              if (data.warning.verbosity == f_console_verbosity_debug) {
+                fprintf(data.warning.to.stream, "%c", f_string_eol[0]);
+                fprintf(data.warning.to.stream, "%s%sThe %s rule '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : "", i == 1 ? "wanted" : "wished for");
+                fprintf(data.warning.to.stream, "%s%s%s%s", data.warning.context.after->string, data.error.notable.before->string, dynamics[i]->array[j].string, data.warning.notable.after->string);
+                fprintf(data.warning.to.stream, "%s' was not found.%s%c", data.warning.context.before->string, data.error.context.after->string, f_string_eol[0]);
+
+                controller_rule_error_print(data.warning, *cache, F_true);
+              }
+            }
+          }
+
+          if (F_status_is_error_not(status) && at < setting->rules.used) {
+
+            // @todo: this will also need to support the asynchronous/wait behavior.
+            //if (setting->rules.array[at].status == F_busy) { ... if wait then block ... }
+
+            // when the status is unknown, then the rule has not been executed, so attempt to execute it.
+            if (setting->rules.array[at].status == F_known_not) {
+
+              status = fl_type_array_lengths_increase_by(controller_default_allocation_step, &cache->stack);
+
+              if (F_status_is_error(status)) {
+                fll_error_print(data.error, F_status_set_fine(status), "fl_type_array_lengths_increase_by", F_true);
+
+                // always exit on memory errors, even in simulate mode.
+                break;
+              }
+
+              // rule execution will re-use the existing cache, so save the current cache.
+              const f_array_length_t cache_line_action = cache->line_action;
+              const f_array_length_t cache_line_item = cache->line_item;
+
+              const f_string_length_t cache_name_action_used = cache->name_action.used;
+              const f_string_length_t cache_name_item_used = cache->name_item.used;
+              const f_string_length_t cache_name_file_used = cache->name_file.used;
+
+              char cache_name_action[cache_name_action_used];
+              char cache_name_item[cache_name_item_used];
+              char cache_name_file[cache_name_file_used];
+
+              memcpy(cache_name_action, cache->name_action.string, cache->name_action.used);
+              memcpy(cache_name_item, cache->name_item.string, cache->name_item.used);
+              memcpy(cache_name_file, cache->name_file.string, cache->name_file.used);
+
+              // @todo: this should pass or use the asynchronous state.
+              status = controller_rule_process(data, at, simulate, setting, cache);
+
+              // restore cache.
+              memcpy(cache->name_action.string, cache_name_action, cache_name_action_used);
+              memcpy(cache->name_item.string, cache_name_item, cache_name_item_used);
+              memcpy(cache->name_file.string, cache_name_file, cache_name_file_used);
+
+              cache->name_action.string[cache_name_action_used] = 0;
+              cache->name_item.string[cache_name_item_used] = 0;
+
+              cache->name_action.used = cache_name_action_used;
+              cache->name_item.used = cache_name_item_used;
+              cache->name_file.used = cache_name_file_used;
+
+              cache->line_action = cache_line_action;
+              cache->line_item = cache_line_item;
+
+              if (F_status_is_error(status)) {
+                if (i == 0 || i == 1 || F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
+
+                  if (data.error.verbosity != f_console_verbosity_quiet) {
+                    fprintf(data.error.to.stream, "%c", f_string_eol[0]);
+                    fprintf(data.error.to.stream, "%s%sThe %s rule '", data.error.context.before->string, data.error.prefix ? data.error.prefix : "", i ? "wanted" : "needed");
+                    fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, dynamics[i]->array[j].string, data.error.notable.after->string);
+                    fprintf(data.error.to.stream, "%s' failed during execution.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol[0]);
+
+                    controller_rule_error_print(data.error, *cache, F_true);
+                  }
+
+                  if (!simulate || F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
+                    break;
+                  }
+                }
+                else {
+                  if (data.warning.verbosity == f_console_verbosity_debug) {
+                    fprintf(data.warning.to.stream, "%c", f_string_eol[0]);
+                    fprintf(data.warning.to.stream, "%s%sThe wished for rule '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : "");
+                    fprintf(data.warning.to.stream, "%s%s%s%s", data.warning.context.after->string, data.error.notable.before->string, dynamics[i]->array[j].string, data.warning.notable.after->string);
+                    fprintf(data.warning.to.stream, "%s' failed during execution.%s%c", data.warning.context.before->string, data.error.context.after->string, f_string_eol[0]);
+
+                    controller_rule_error_print(data.warning, *cache, F_true);
+                  }
+                }
+              }
+            }
+            else if (F_status_is_error(setting->rules.array[at].status)) {
+
+              if (i == 0 || i == 1) {
+
+                if (data.error.verbosity != f_console_verbosity_quiet) {
+                  fprintf(data.error.to.stream, "%c", f_string_eol[0]);
+                  fprintf(data.error.to.stream, "%s%sThe %s rule '", data.error.context.before->string, data.error.prefix ? data.error.prefix : "", i ? "wanted" : "needed");
+                  fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, dynamics[i]->array[j].string, data.error.notable.after->string);
+                  fprintf(data.error.to.stream, "%s' is in a failed state.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol[0]);
+
+                  controller_rule_error_print(data.error, *cache, F_true);
+                }
+
+                status = F_status_set_error(F_found_not);
+
+                if (!simulate) break;
+              }
+              else {
+                if (data.warning.verbosity == f_console_verbosity_debug) {
+                  fprintf(data.warning.to.stream, "%c", f_string_eol[0]);
+                  fprintf(data.warning.to.stream, "%s%sThe wished for rule '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : "");
+                  fprintf(data.warning.to.stream, "%s%s%s%s", data.warning.context.after->string, data.error.notable.before->string, dynamics[i]->array[j].string, data.warning.notable.after->string);
+                  fprintf(data.warning.to.stream, "%s' is in a failed state.%s%c", data.warning.context.before->string, data.error.context.after->string, f_string_eol[0]);
+
+                  controller_rule_error_print(data.warning, *cache, F_true);
+                }
+              }
+            }
+          }
+        }
+
+        if (F_status_is_error(status)) break;
+      } // for
+    }
+
+    if (F_status_is_error_not(status)) {
+      if (simulate) {
+        //status = controller_rule_simulate();
+      }
+      else {
+        //status = controller_rule_execute();
+      }
+    }
+
+    // remove this rule off the stack.
+    cache->stack.used--;
+
+    if (F_status_is_error(status)) {
+      return status;
+    }
+
+    return F_none;
+  }
+#endif // _di_controller_rule_process_
+
 #ifndef _di_controller_rule_setting_read_
   f_return_status controller_rule_setting_read(const controller_data_t data, controller_cache_t *cache, controller_rule_t *rule) {
     f_status_t status = F_none;
index 71b2ede1fab1db1fe023443a20547fb680936774..ef1abc6f78351445e459398a9adbcd133a1682b0 100644 (file)
@@ -225,6 +225,37 @@ extern "C" {
 #endif // _di_controller_rule_read_
 
 /**
+ * Process and execute the given rule by the rule id.
+ *
+ * Any dependent rules are loaded and executed as per "need", "want", and "wish" rule settings.
+ * All dependent rules must be already loaded, this function will not load any rules.
+ *
+ * This function is recursively called for each "need", "want", and "wish", and has a max recursion length of the max size of the f_array_lengths_t array.
+ *
+ * @todo add asynchronous boolean? (will also need a wait boolean, so this should probably be a uint8_t with using bitwise states).
+ *
+ * @param data
+ *   The program data.
+ * @param index
+ *   Position in the rules array representing the rule to execute
+ * @param simulate
+ *   If TRUE, then the rule execution is simulated (printing a message that the rule would be executed but does not execut the rule).
+ *   If FALSE, the rule is not simulated and is executed as normal.
+ * @param setting
+ *   The controller settings data.
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ *   This utilizes cache.stack for recursive executions, no function called by this may therefore safely use cache.stack for any other purpose.
+ *   This utilizes line_action, line_item, name_action, and name_item from cache, but they are backed up before starting and then restored after finishing.
+ *
+ * @return
+ *    F_none on success.
+ */
+#ifndef _di_controller_rule_process_
+  extern f_return_status controller_rule_process(const controller_data_t data, const f_array_length_t index, const bool simulate, controller_setting_t *setting, controller_cache_t *cache) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_process_
+
+/**
  * Read the content within the buffer, extracting all valid settings.
  *
  * This will perform additional FSS read functions as appropriate.