]> Kevux Git Server - fll/commitdiff
Progress: controller program.
authorKevin Day <thekevinday@gmail.com>
Sun, 11 Apr 2021 05:01:36 +0000 (00:01 -0500)
committerKevin Day <thekevinday@gmail.com>
Sun, 11 Apr 2021 05:01:36 +0000 (00:01 -0500)
I completely overlooked that the thread time related functions are relative to the absolute system clock.
The timed locks were all acting crazy because I used relative values!

The process status is not being initialized, resulting in invalid and error prone checks.

Begin fixing problems with the asynchronous processing.
Between incorrect uses of times and some stuctural problems, the asynchronous behavior was not operating in the order of the dependencies.
The current work fixes that, but there is still more work to do.
I expect this code to not work just yet.

There also seems to be a locking issue somewhere in here that prevents the program from fully exiting.
Check the thread conditions as those have had problems in the past.
.

20 files changed:
level_3/controller/c/controller.h
level_3/controller/c/private-common.c
level_3/controller/c/private-common.h
level_3/controller/c/private-rule.c
level_3/controller/c/private-thread.c
level_3/controller/data/settings/example/entries/asynchronous-serial.entry [new file with mode: 0644]
level_3/controller/data/settings/example/entries/asynchronous.entry
level_3/controller/data/settings/example/entries/serial.entry [new file with mode: 0644]
level_3/controller/data/settings/example/rules/asynchronous/sleep_1.rule
level_3/controller/data/settings/example/rules/asynchronous/sleep_10.rule
level_3/controller/data/settings/example/rules/asynchronous/sleep_2.rule
level_3/controller/data/settings/example/rules/asynchronous/sleep_3.rule
level_3/controller/data/settings/example/rules/asynchronous/sleep_5.rule
level_3/controller/data/settings/example/rules/asynchronous/sleep_8.rule
level_3/controller/data/settings/example/rules/serial/s_1.rule [new file with mode: 0644]
level_3/controller/data/settings/example/rules/serial/s_2.rule [new file with mode: 0644]
level_3/controller/data/settings/example/rules/serial/s_3.rule [new file with mode: 0644]
level_3/controller/data/settings/example/rules/serial/s_4.rule [new file with mode: 0644]
level_3/controller/data/settings/example/rules/serial/s_5.rule [new file with mode: 0644]
level_3/controller/data/settings/example/rules/serial/s_6.rule [new file with mode: 0644]

index 0cc1f62be90a111df387b3c52cc00e0d6728c61d..920f4d6fb0b2f87362042adb8c15b67f9b826546 100644 (file)
@@ -27,6 +27,8 @@
 // libc includes
 #include <sched.h>
 #include <string.h>
+#include <time.h>
+#include <sys/time.h>
 #include <sys/types.h>
 #include <linux/sched.h>
 #include <unistd.h>
index 888cec0240a59e40314cdd25e42210f25fd2885a..873ffc8cc173214ce605ad7d4a2619dc5827c969 100644 (file)
@@ -164,12 +164,13 @@ extern "C" {
   f_status_t controller_lock_write(controller_thread_t * const thread, f_thread_lock_t *lock) {
 
     struct timespec time;
-    time.tv_sec = 0;
-    time.tv_nsec = controller_thread_lock_timeout;
 
     f_status_t status = F_none;
 
     for (;;) {
+
+      controller_time(0, controller_thread_lock_timeout, &time);
+
       status = f_thread_lock_write_timed(&time, lock);
 
       if (status == F_time) {
@@ -223,35 +224,41 @@ extern "C" {
 #endif // _di_controller_process_delete_simple_
 
 #ifndef _di_controller_process_wait_
-  void controller_process_wait(const controller_main_t main, controller_process_t *process) {
+  f_status_t controller_process_wait(const controller_main_t main, controller_process_t *process) {
 
-    if (!main.thread->enabled) return;
+    if (!main.thread->enabled) {
+      return F_signal;
+    }
 
     struct timespec time;
-    time.tv_sec = controller_thread_wait_timeout_seconds;
-    time.tv_nsec = controller_thread_wait_timeout_nanoseconds;
 
     f_status_t status = F_none;
 
     do {
       f_thread_mutex_lock(&process->wait_lock);
 
+      controller_time(controller_thread_wait_timeout_seconds, controller_thread_wait_timeout_nanoseconds, &time);
+
       status = f_thread_condition_wait_timed(&time, &process->wait, &process->wait_lock);
 
       f_thread_mutex_unlock(&process->wait_lock);
 
-      if (!main.thread->enabled) break;
+      if (!main.thread->enabled) {
+        return F_signal;
+      }
 
       f_thread_lock_read(&process->lock);
 
       if (process->status != F_known_not || !(process->state == controller_process_state_active || process->state == controller_process_state_busy)) {
         f_thread_unlock(&process->lock);
 
-        break;
+        return F_none;
       }
 
       f_thread_unlock(&process->lock);
-    } while (main.thread->enabled);
+    } while (status == F_time && main.thread->enabled);
+
+    return status;
   }
 #endif // _di_controller_process_wait_
 
@@ -330,6 +337,10 @@ extern "C" {
           processs->size = length;
           return status;
         }
+        else {
+          process->status = F_known_not;
+          process->rule.status = F_known_not;
+        }
       } // for
 
       processs->size = length;
@@ -486,6 +497,18 @@ extern "C" {
   }
 #endif // _di_controller_thread_delete_simple_
 
+#ifndef _di_controller_time_
+  void controller_time(const time_t seconds, const long nanos, struct timespec *time) {
+
+    struct timeval now;
+
+    gettimeofday(&now, 0);
+
+    time->tv_sec = now.tv_sec + seconds;
+    time->tv_nsec = now.tv_usec * 1000 + nanos;
+  }
+#endif // _di_controller_time_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index 32b6321cf24b7a6482e40fb6eea5ca51d15f4686..9b2f91f7d851525609a27fe6ed638f641431e326 100644 (file)
@@ -1079,7 +1079,13 @@ extern "C" {
   #define controller_thread_exit_process_cancel_total 150 // 9 seconds in multiples of wait.
   #define controller_thread_lock_timeout 100000000 // 0.1 seconds in nanoseconds.
   #define controller_thread_simulation_timeout 200000 // 0.2 seconds in microseconds.
-  #define controller_thread_wait_timeout_seconds 10
+
+   // @todo implement a staged incrementing wait that waits a short amount of time for say 3 times, then waits a longer amount of time for say 3 times, and then wait a long period of time.
+   //       example: wait 0.02 seconds for 4 times, then
+   //                wait 0.2 seconds for 8 times, then
+   //                wait 2 seconds for 16 times, then
+   //                wait 20 seconds for every time thereafter.
+  #define controller_thread_wait_timeout_seconds 5
   #define controller_thread_wait_timeout_nanoseconds 0
 
   typedef struct {
@@ -1378,10 +1384,18 @@ extern "C" {
  * @param process
  *   The process to wait on.
  *
+ * @return
+ *   F_none on success.
+ *   F_signal on success and signal found.
+ *
+ *   Success from f_thread_condition_wait_timed().
+ *
+ *   Errors (with error bit) from: f_thread_condition_wait_timed().
+ *
  * @see f_thread_condition_wait_timed()
  */
 #ifndef _di_controller_process_wait_
-  extern void controller_process_wait(const controller_main_t main, controller_process_t *process) f_gcc_attribute_visibility_internal;
+  extern f_status_t controller_process_wait(const controller_main_t main, controller_process_t *process) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_process_wait_
 
 /**
@@ -1598,6 +1612,23 @@ extern "C" {
   extern void controller_thread_delete_simple(controller_thread_t *thread) f_gcc_attribute_visibility_internal;
 #endif // _di_controller_thread_delete_simple_
 
+/**
+ * Get the current time, plus the given offset.
+ *
+ * @todo this is basic enough that there needs to be an f_time class with this function f_time_now(), f_time_future(), f_time_past().
+ *       "struct timespec" -> f_time_nano_t, "struct timeval" -> f_time_micro_t.
+ *
+ * @param seconds
+ *   The seconds to add to current time.
+ * @param nanos
+ *   The nanoseconds to add to current time.
+ * @param time
+ *   The resulting current time.
+ */
+#ifndef _di_controller_time_
+  void controller_time(const time_t seconds, const long nanos, struct timespec *time) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_time_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index 764f090e11fa699a6ec6036d2631e3cc34617717..33304e29bb1c49a8e4b653548db710c6e7e7ce44 100644 (file)
@@ -1705,10 +1705,14 @@ extern "C" {
       f_array_length_t k = 0;
       f_array_length_t id_rule = 0;
       f_array_length_t id_process = 0;
+
       bool found = F_false;
+      bool busy = F_false;
 
       controller_process_t *process_other = 0;
 
+      uint8_t rule_options = 0;
+
       f_string_dynamics_t * const dynamics[] = {
         &process->rule.need,
         &process->rule.want,
@@ -1727,10 +1731,19 @@ extern "C" {
 
         for (j = 0; j < dynamics[i]->used; ++j) {
 
+          process_other = 0;
+          found = F_false;
+
           f_thread_lock_read(&main.thread->lock.process);
 
           status = controller_find_process(dynamics[i]->array[j], main.thread->processs, &id_process);
 
+          if (status == F_true) {
+            process_other = main.thread->processs.array[id_process];
+
+            f_thread_lock_read(&process_other->active);
+          }
+
           f_thread_unlock(&main.thread->lock.process);
 
           if (status == F_true) {
@@ -1762,6 +1775,10 @@ extern "C" {
               if (!(process->options & controller_rule_option_simulate)) {
                 f_thread_unlock(&main.thread->lock.rule);
 
+                if (process_other) {
+                  f_thread_unlock(&process_other->active);
+                }
+
                 break;
               }
             }
@@ -1775,54 +1792,50 @@ extern "C" {
                 controller_print_unlock_flush(main.data->output.stream, &main.thread->lock.print);
               }
             }
-
-            f_thread_unlock(&main.thread->lock.rule);
+          }
+          else {
+            f_thread_lock_read(&main.thread->lock.rule);
           }
 
-          f_thread_lock_read(&main.thread->lock.rule);
+          if (found) {
 
-          if (status == F_true && found) {
-            f_thread_unlock(&main.thread->lock.rule);
-            f_thread_lock_read(&main.thread->lock.process);
+            // the process_other may have write locks, which needs to be avoided, so copy the alias from the rule.
+            char alias_other_buffer[main.setting->rules.array[id_rule].alias.used + 1];
 
-            process_other = main.thread->processs.array[id_process];
+            memcpy(alias_other_buffer, main.setting->rules.array[id_rule].alias.string, sizeof(char) * main.setting->rules.array[id_rule].alias.used);
+            alias_other_buffer[main.setting->rules.array[id_rule].alias.used] = 0;
 
-            f_thread_lock_read(&process_other->active);
-            f_thread_lock_read(&process_other->lock);
+            const f_string_static_t alias_other = f_macro_string_static_t_initialize(alias_other_buffer, main.setting->rules.array[id_rule].alias.used);
 
-            if (process_other->status == F_known_not && (process_other->state == controller_process_state_active || process_other->state == controller_process_state_busy)) {
-              f_thread_unlock(&process_other->lock);
+            f_thread_unlock(&main.thread->lock.rule);
 
-              controller_process_wait(main, process_other);
+            busy = F_false;
+
+            status = f_thread_lock_read_try(&process_other->lock);
+
+            if (status == F_busy) {
+              busy = F_true;
             }
             else {
+              if (process_other->status == F_known_not || process_other->state == controller_process_state_active || process_other->state == controller_process_state_busy) {
+                busy = F_true;
+              }
+
               f_thread_unlock(&process_other->lock);
             }
 
-            f_thread_unlock(&main.thread->lock.process);
-
-            if (!main.thread->enabled) {
-              f_thread_unlock(&process_other->active);
-
-              status = F_signal;
-              break;
+            if (busy) {
+              controller_process_wait(main, process_other);
             }
+            else {
+              rule_options = 0;
 
-            f_thread_lock_read(&main.thread->lock.rule);
-
-            char alias_other_buffer[main.setting->rules.array[id_rule].alias.used + 1];
-
-            memcpy(alias_other_buffer, main.setting->rules.array[id_rule].alias.string, sizeof(char) * main.setting->rules.array[id_rule].alias.used);
-            alias_other_buffer[main.setting->rules.array[id_rule].alias.used] = 0;
-
-            // attempt to (synchronously) execute the rule when the status is unknown (the rule has not yet been run).
-            if (main.setting->rules.array[id_rule].status == F_known_not) {
-
-              const f_string_static_t alias_other = f_macro_string_static_t_initialize(alias_other_buffer, main.setting->rules.array[id_rule].alias.used);
-
-              f_thread_unlock(&main.thread->lock.rule);
+              if (main.data->parameters[controller_parameter_test].result == f_console_result_found) {
+                rule_options |= controller_rule_option_simulate;
+              }
 
-              status = controller_rule_process_begin(controller_process_option_execute, alias_other, action, process->options & controller_rule_option_asynchronous ? process->options - controller_rule_option_asynchronous : process->options, process->stack, main, process->cache);
+              // synchronously execute dependency.
+              status = controller_rule_process_begin(controller_process_option_execute, alias_other, controller_rule_action_type_start, controller_process_option_execute, process->stack, main, process_other->cache);
 
               if (status == F_child || status == F_signal) {
                 f_thread_unlock(&process_other->active);
@@ -1839,7 +1852,7 @@ extern "C" {
 
                   controller_print_unlock_flush(main.data->output.stream, &main.thread->lock.print);
 
-                  if (!(process->options & controller_rule_option_simulate) || F_status_set_fine(status) == F_memory_not) {
+                  if (!(process_other->options & controller_rule_option_simulate) || F_status_set_fine(status) == F_memory_not) {
                     f_thread_unlock(&process_other->active);
 
                     break;
@@ -1856,10 +1869,16 @@ extern "C" {
                   }
                 }
               }
+            }
 
-              f_thread_lock_read(&main.thread->lock.rule);
+            if (!main.thread->enabled) {
+              f_thread_unlock(&process_other->active);
+
+              break;
             }
 
+            f_thread_lock_read(&main.thread->lock.rule);
+
             if (F_status_is_error(main.setting->rules.array[id_rule].status)) {
               if (i == 0 || i == 1) {
                 f_thread_mutex_lock(&main.thread->lock.print);
@@ -1871,9 +1890,13 @@ extern "C" {
 
                 controller_print_unlock_flush(main.data->output.stream, &main.thread->lock.print);
 
-                if (!(process->options & controller_rule_option_simulate)) {
+                if (!(process_other->options & controller_rule_option_simulate)) {
                   f_thread_unlock(&main.thread->lock.rule);
-                  f_thread_unlock(&process_other->active);
+
+                  if (process_other) {
+                    f_thread_unlock(&process_other->active);
+                  }
+
                   break;
                 }
               }
@@ -1890,12 +1913,15 @@ extern "C" {
             }
 
             f_thread_unlock(&main.thread->lock.rule);
-            f_thread_unlock(&process_other->active);
           }
           else {
             f_thread_unlock(&main.thread->lock.rule);
           }
 
+          if (process_other) {
+            f_thread_unlock(&process_other->active);
+          }
+
           if (!main.thread->enabled) break;
         } // for
 
@@ -1915,8 +1941,8 @@ extern "C" {
       return F_signal;
     }
 
-    if (!(process->options & controller_rule_option_wait) && F_status_is_error_not(status)) {
-      controller_rule_wait_all(main, process);
+    if ((process->options & controller_rule_option_wait) && F_status_is_error_not(status)) {
+      controller_rule_wait_all(main, process); // @fixme review this, it needs to check anything depending on itself!
 
       if (!main.thread->enabled) {
         return F_signal;
@@ -2497,6 +2523,11 @@ extern "C" {
 
     process->stack.used = used_original_stack;
 
+    // inform all things waiting that the process has finished running.
+    f_thread_mutex_lock(&process->wait_lock);
+    f_thread_condition_signal_all(&process->wait);
+    f_thread_mutex_unlock(&process->wait_lock);
+
     f_thread_unlock(&process->lock);
 
     if (options & controller_process_option_asynchronous) {
@@ -2504,19 +2535,10 @@ extern "C" {
     }
 
     if (main.thread->enabled) {
-      if (options & controller_process_option_execute) {
-
-        // inform all things waiting that the process has finished running.
-        f_thread_mutex_lock(&process->wait_lock);
-        f_thread_condition_signal_all(&process->wait);
-        f_thread_mutex_unlock(&process->wait_lock);
-      }
-    }
-    else {
-      return F_signal;
+      return status;
     }
 
-    return status;
+    return F_signal;
   }
 #endif // _di_controller_rule_process_do_
 
@@ -4888,7 +4910,7 @@ extern "C" {
       if (caller) {
         f_thread_lock_read(&main.thread->lock.rule);
 
-        for (j = 0; j < caller->stack.used; ++j) {
+        for (j = 0; j < caller->stack.used && main.thread->enabled; ++j) {
 
           if (main.thread->processs.array[caller->stack.array[j]] && fl_string_dynamic_compare(process->rule.alias, main.thread->processs.array[caller->stack.array[j]]->rule.alias) == F_equal_to) {
             skip = F_true;
@@ -4899,6 +4921,7 @@ extern "C" {
 
         f_thread_unlock(&main.thread->lock.rule);
 
+        if (!main.thread->enabled) break;
         if (skip) continue;
       }
 
@@ -4927,16 +4950,12 @@ extern "C" {
           }
 
           if (process->state == controller_process_state_done) {
-            f_thread_unlock(&process->active);
-
             if (f_thread_lock_write_try(&process->active) == F_none) {
               f_thread_join(process->id_thread, 0);
+
               process->state = controller_process_state_idle;
               process->id_thread = 0;
             }
-            else {
-              f_thread_lock_read(&process->active);
-            }
           }
         }
 
index bb23ee32583110776c5f936a604ab6901f7a9f77..bd273bb8c03eab886404741158b8292534c07c50 100644 (file)
@@ -350,9 +350,7 @@ extern "C" {
     f_status_t status = F_none;
     f_array_length_t spent = 0;
 
-    struct timespec wait;
-    wait.tv_sec = 0;
-    wait.tv_nsec = controller_thread_exit_process_cancel_wait;
+    struct timespec time;
 
     controller_process_t *process = 0;
 
@@ -388,7 +386,9 @@ extern "C" {
       process = main->thread->processs.array[i];
 
       if (process->id_thread) {
-        status = f_thread_join_timed(process->id_thread, wait, 0);
+        controller_time(0, controller_thread_exit_process_cancel_wait, &time);
+
+        status = f_thread_join_timed(process->id_thread, time, 0);
 
         if (status == F_none) {
           process->child = 0;
@@ -407,7 +407,9 @@ extern "C" {
       process = main->thread->processs.array[i];
 
       do {
-        status = f_thread_join_timed(process->id_thread, wait, 0);
+        controller_time(0, controller_thread_exit_process_cancel_wait, &time);
+
+        status = f_thread_join_timed(process->id_thread, time, 0);
 
         if (status == F_none) {
           process->child = 0;
@@ -429,7 +431,10 @@ extern "C" {
         if (process->child > 0) {
           f_signal_send(F_signal_kill, process->child);
 
-          nanosleep(&wait, 0);
+          time.tv_sec = 0;
+          time.tv_nsec = controller_thread_exit_process_cancel_wait;
+
+          nanosleep(&time, 0);
         }
 
         f_thread_join(process->id_thread, 0);
diff --git a/level_3/controller/data/settings/example/entries/asynchronous-serial.entry b/level_3/controller/data/settings/example/entries/asynchronous-serial.entry
new file mode 100644 (file)
index 0000000..d84da9b
--- /dev/null
@@ -0,0 +1,12 @@
+# fss-0005
+
+main:
+
+  rule serial s_1 asynchronous
+  rule serial s_2 asynchronous
+  rule serial s_3 asynchronous
+  rule serial s_4 asynchronous
+  rule serial s_5 asynchronous
+  rule serial s_6 asynchronous
+
+  ready
index 5a2f516cbff78d114be2d92bcbdd20f36dbd28cd..3b189f8589c958ef1ddeb693fe56e36a1be3d38e 100644 (file)
@@ -1,8 +1,8 @@
 # fss-0005
 
 main:
-  consider asynchronous sleep_3 asynchronous
-  consider asynchronous sleep_5 asynchronous
+  consider asynchronous sleep_8 asynchronous
+  consider asynchronous sleep_10 asynchronous
 
   rule asynchronous sleep_1 asynchronous
   rule asynchronous sleep_2 asynchronous
diff --git a/level_3/controller/data/settings/example/entries/serial.entry b/level_3/controller/data/settings/example/entries/serial.entry
new file mode 100644 (file)
index 0000000..fcfcc82
--- /dev/null
@@ -0,0 +1,13 @@
+# fss-0005
+
+main:
+
+  consider serial s_1
+  consider serial s_2
+  consider serial s_3
+  consider serial s_4
+  consider serial s_5
+
+  rule serial s_6
+
+  ready
index 17671c181eb76ac889816b6a0bacc7e66d719c32..e055c16293ea7df2557dc5615f61319cdcbb6ca3 100644 (file)
@@ -4,10 +4,13 @@ setting:
   name "Sleep 1 Seconds."
   nice 15
   limit nice 1 2
-  need asynchronous sleep_3
+  need asynchronous sleep_10
+
+script:
+  start echo "Sleeping 1: $(date -u)"
 
 script:
   start sleep 1
 
 script:
-  start echo "Sleep 1: $(date -u)"
+  start echo "Slept 1: $(date -u)"
index d4e89b01790aa21ff91d3b4aee52a2b7e229f398..63d644cc5e9d68ca14fef38cd48f1f99d9f2cf34 100644 (file)
@@ -6,7 +6,10 @@ setting:
   limit nice 1 2
 
 script:
+  start echo "Sleeping 10: $(date -u)"
+
+script:
   start sleep 10
 
 script:
-  start echo "Sleep 10: $(date -u)"
+  start echo "Slept 10: $(date -u)"
index a080fd127856094651f1c0138d7a86096aaa1fca..77cbab58f77359d8da47441757b2fd54f54cd7ff 100644 (file)
@@ -4,10 +4,13 @@ setting:
   name "Sleep 2 Seconds."
   nice 15
   limit nice 1 2
-  need asynchronous sleep_5
+  need asynchronous sleep_10
+
+script:
+  start echo "Sleeping 2: $(date -u)"
 
 script:
   start sleep 2
 
 script:
-  start echo "Sleep 2: $(date -u)"
+  start echo "Slept 2: $(date -u)"
index 436fbbdab78b875ffd2b146a31f822980bfd263f..de9141228f502f844ad71219d65bcf47a79d764c 100644 (file)
@@ -4,10 +4,13 @@ setting:
   name "Sleep 3 Seconds."
   nice 15
   limit nice 1 2
-  need asynchronous sleep_5
+  need asynchronous sleep_8
+
+script:
+  start echo "Sleeping 3: $(date -u)"
 
 script:
   start sleep 3
 
 script:
-  start echo "Sleep 3: $(date -u)"
+  start echo "Slept 3: $(date -u)"
index e6ede53279334a5e2a185c212ef4dfa3fb51a0fd..ecac2f525dc718d26d4b8bb7afd1f3042c79ce98 100644 (file)
@@ -6,7 +6,10 @@ setting:
   limit nice 1 2
 
 script:
+  start echo "Sleeping 5: $(date -u)"
+
+script:
   start sleep 5
 
 script:
-  start echo "Sleep 5: $(date -u)"
+  start echo "Slept 5: $(date -u)"
index 1c5dbfce0622f06b6958689396257db0bd8bfcc8..34d919befeca27428430909e0d9d573eaa7c1386 100644 (file)
@@ -6,7 +6,10 @@ setting:
   limit nice 1 2
 
 script:
+  start echo "Sleeping 8: $(date -u)"
+
+script:
   start sleep 8
 
 script:
-  start echo "Sleep 8: $(date -u)"
+  start echo "Slept 8: $(date -u)"
diff --git a/level_3/controller/data/settings/example/rules/serial/s_1.rule b/level_3/controller/data/settings/example/rules/serial/s_1.rule
new file mode 100644 (file)
index 0000000..f434357
--- /dev/null
@@ -0,0 +1,10 @@
+# fss-000d
+
+setting:
+  name "Serial 1"
+
+script:
+  start {
+    echo "Serial 1: $(date -u)"
+    sleep 1
+  }
diff --git a/level_3/controller/data/settings/example/rules/serial/s_2.rule b/level_3/controller/data/settings/example/rules/serial/s_2.rule
new file mode 100644 (file)
index 0000000..1846b09
--- /dev/null
@@ -0,0 +1,11 @@
+# fss-000d
+
+setting:
+  name "Serial 2"
+  need serial s_1
+
+script:
+  start {
+    echo "Serial 2: $(date -u)"
+    sleep 1
+  }
diff --git a/level_3/controller/data/settings/example/rules/serial/s_3.rule b/level_3/controller/data/settings/example/rules/serial/s_3.rule
new file mode 100644 (file)
index 0000000..9b0ef02
--- /dev/null
@@ -0,0 +1,11 @@
+# fss-000d
+
+setting:
+  name "Serial 3"
+  need serial s_2
+
+script:
+  start {
+    echo "Serial 3: $(date -u)"
+    sleep 1
+  }
diff --git a/level_3/controller/data/settings/example/rules/serial/s_4.rule b/level_3/controller/data/settings/example/rules/serial/s_4.rule
new file mode 100644 (file)
index 0000000..46217ab
--- /dev/null
@@ -0,0 +1,11 @@
+# fss-000d
+
+setting:
+  name "Serial 4"
+  need serial s_3
+
+script:
+  start {
+    echo "Serial 4: $(date -u)"
+    sleep 1
+  }
diff --git a/level_3/controller/data/settings/example/rules/serial/s_5.rule b/level_3/controller/data/settings/example/rules/serial/s_5.rule
new file mode 100644 (file)
index 0000000..f180252
--- /dev/null
@@ -0,0 +1,11 @@
+# fss-000d
+
+setting:
+  name "Serial 5"
+  need serial s_4
+
+script:
+  start {
+    echo "Serial 5: $(date -u)"
+    sleep 1
+  }
diff --git a/level_3/controller/data/settings/example/rules/serial/s_6.rule b/level_3/controller/data/settings/example/rules/serial/s_6.rule
new file mode 100644 (file)
index 0000000..ec3b020
--- /dev/null
@@ -0,0 +1,11 @@
+# fss-000d
+
+setting:
+  name "Serial 6"
+  need serial s_5
+
+script:
+  start {
+    echo "Serial 6: $(date -u)"
+    sleep 1
+  }