]> 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 0cc1f62..920f4d6 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 888cec0..873ffc8 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 32b6321..9b2f91f 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 764f090..33304e2 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 bb23ee3..bd273bb 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 5a2f516..3b189f8 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 17671c1..e055c16 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 d4e89b0..63d644c 100644 (file)
@@ -5,8 +5,11 @@ setting:
   nice 15
   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 a080fd1..77cbab5 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 436fbbd..de91412 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 e6ede53..ecac2f5 100644 (file)
@@ -5,8 +5,11 @@ setting:
   nice 15
   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 1c5dbfc..34d919b 100644 (file)
@@ -5,8 +5,11 @@ setting:
   nice 15
   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
+  }