From fae32b062cc8a37222ca16123d7f781243b51670 Mon Sep 17 00:00:00 2001
From: Brendan Bartels <bbartels@iastate.edu>
Date: Fri, 21 Apr 2017 12:50:49 -0500
Subject: [PATCH] quad: improve virtual quad cli

---
 quad/scripts/tests/test_communication.rb    |   8 +-
 quad/scripts/tests/test_logging.rb          |   4 +-
 quad/scripts/tests/test_memory_integrity.rb |   2 +-
 quad/scripts/tests/test_safety_checks.rb    |   2 +-
 quad/src/virt_quad/README.md                |  15 +-
 quad/src/virt_quad/main.c                   | 223 ++++++++++++++------
 6 files changed, 176 insertions(+), 78 deletions(-)

diff --git a/quad/scripts/tests/test_communication.rb b/quad/scripts/tests/test_communication.rb
index 4887a29bc..8797cc16d 100644
--- a/quad/scripts/tests/test_communication.rb
+++ b/quad/scripts/tests/test_communication.rb
@@ -16,7 +16,7 @@ Timeout::timeout(30) {
   puts("Setting up...")
 
   # Start virtual quad
-  quad_pid = Process.spawn("./virt-quad -q",
+  quad_pid = Process.spawn("./virt-quad start -q",
                            { :rlimit_as => 536870912, # 512 MiB total RAM
                              :rlimit_stack => 1048576}) # 1 MiB stack
 
@@ -49,7 +49,7 @@ Timeout::timeout(30) {
 
       # Send a debug command
       Thread.new {
-        sleep 0.1
+        sleep 0.5
         send_packet [0xBE, 1, 0, 0, 0, 0, 0, 0xBF]
       }
 
@@ -58,7 +58,7 @@ Timeout::timeout(30) {
       # Receive the header
       msg = []
       for i in 1..7
-        sleep 0.0001
+        sleep 0.01
         c = fifo.read(1)
         msg.push(c)
       end
@@ -68,7 +68,7 @@ Timeout::timeout(30) {
 
       msg = []
       for i in 1..length
-        sleep 0.0001
+        sleep 0.01
         c = fifo.read(1)
         msg.push(c)
       end
diff --git a/quad/scripts/tests/test_logging.rb b/quad/scripts/tests/test_logging.rb
index a0e86e716..58d16f73c 100644
--- a/quad/scripts/tests/test_logging.rb
+++ b/quad/scripts/tests/test_logging.rb
@@ -16,7 +16,7 @@ Timeout::timeout(30) {
   puts("Setting up...")
 
   # Start virtual quad
-  quad_pid = Process.spawn("./virt-quad -q",
+  quad_pid = Process.spawn("./virt-quad start -q",
                            { :rlimit_as => 536870912, # 512 MiB total RAM
                              :rlimit_stack => 1048576}) # 1 MiB stack
 
@@ -83,7 +83,7 @@ Timeout::timeout(30) {
     # Get logs
 
     Thread.new {
-      sleep 0.5
+      sleep 1
       puts("Swiching off GEAR...")
       set_gear GEAR_OFF
     }
diff --git a/quad/scripts/tests/test_memory_integrity.rb b/quad/scripts/tests/test_memory_integrity.rb
index d7116c1d4..adb8f9190 100644
--- a/quad/scripts/tests/test_memory_integrity.rb
+++ b/quad/scripts/tests/test_memory_integrity.rb
@@ -14,7 +14,7 @@ Dir.chdir(bin_dir)
 puts("Firing up the quad...")
 
 # Start virtual quad
-quad = Process.spawn("valgrind --leak-check=full --log-file=./valgrind.out ./virt-quad")
+quad = Process.spawn("valgrind --leak-check=full --log-file=./valgrind.out ./virt-quad start")
 
 sleep 1.5
 
diff --git a/quad/scripts/tests/test_safety_checks.rb b/quad/scripts/tests/test_safety_checks.rb
index c5557cece..c96a8228f 100644
--- a/quad/scripts/tests/test_safety_checks.rb
+++ b/quad/scripts/tests/test_safety_checks.rb
@@ -17,7 +17,7 @@ Timeout::timeout(60) {
   puts("Setting up...")
 
   # Start virtual quad
-  quad_pid = Process.spawn("./virt-quad -q",
+  quad_pid = Process.spawn("./virt-quad start -q",
                            { :rlimit_as => 536870912, # 512 MiB total RAM
                              :rlimit_stack => 1048576}) # 1 MiB stack
 
diff --git a/quad/src/virt_quad/README.md b/quad/src/virt_quad/README.md
index 3607ec44c..3b263e528 100644
--- a/quad/src/virt_quad/README.md
+++ b/quad/src/virt_quad/README.md
@@ -8,17 +8,18 @@ plethoria of things required for flight in Coover 3050. In fact, you don't
 even have to be in Coover 3050...
 
 # Using the Virtual Quad
-Start it up:
+The virt-quad has help output. Get started with:
 ```
-make run
+./virt-quad
 ```
 
-And you can do things with it. You'll notice it will make a bunch of FIFOs in
-the current directory. Write to / read from these FIFOs as you see fit.
+## Using the UART Driver
 
-## Writing to FIFO from shell
+The UART interface is implemented with unix FIFOs. You can treat these FIFOs
+as regular unix files. Read from uart-tx to hear with the quad is saying. Write
+to uart-rx to tell the quad something.
 
 ```
-echo "170000" > virt-quad-fifos/pwm-input-gear  # Setting the gear to '1'
-cat virt-quad-fifos/pwm-output-motor1           # Read the motor 1 pwm signal
+echo "hello world" > virt-quad-fifos/uart-rx
+cat virt-quad-fifos/uart-tx
 ```
\ No newline at end of file
diff --git a/quad/src/virt_quad/main.c b/quad/src/virt_quad/main.c
index 105a8a5e6..1e24ab458 100644
--- a/quad/src/virt_quad/main.c
+++ b/quad/src/virt_quad/main.c
@@ -4,15 +4,28 @@
 #include "quad_app.h"
 #include <fcntl.h>
 
+struct VirtQuadIO *virt_quad_io;
+int virt_quad_io_file_descriptor;
+
+enum CLISubCommand {
+  START,
+  GET,
+  SET,
+  USAGE,
+  HELP,
+};
+
+int start_virt_quad(char *argv[]);
 int handle_io_output(const char *name);
 int handle_io_input(const char *name, const char *value_str);
 void set_shm(float value, float *dest, pthread_mutex_t *lock);
 void print_shm_float(float *value, pthread_mutex_t *lock);
 void print_shm_int(int *value, pthread_mutex_t *lock);
 void usage(char *executable_name);
-
-struct VirtQuadIO *virt_quad_io;
-int virt_quad_io_file_descriptor;
+enum CLISubCommand parse_sub_command(char *argv[]);
+void open_new_shm_io();
+void open_existing_shm_io();
+void help_sub_command(char *argv[]);
 
 /**
  * Implement each of the hardware interfaces.
@@ -33,61 +46,91 @@ int setup_hardware(hardware_t *hardware) {
 }
 
 int main(int argc, char *argv[]) {
-  int fd;
+  enum CLISubCommand sub_command = parse_sub_command(argv + 1);
+
+  switch (sub_command) {
+  case START:
+    open_new_shm_io();
+    start_virt_quad(argv + 2); // will block
+    break;
+  case SET:
+    open_existing_shm_io();
+    handle_io_input(argv[2], argv[3]);
+    break;
+  case GET:
+    open_existing_shm_io();
+    handle_io_output(argv[2]);
+    break;
+  case HELP:
+    help_sub_command(argv);
+    break;
+  case USAGE:
+    usage(argv[0]);
+    break;
+  }
 
-  // Decide if we are launching the quad or parsing arguments
-  if (argv[1] == NULL || strcmp(argv[1], "-q") == 0) {
-    // launch the quad
-
-    // Allow making the quad quiet
-    if (argv[1] != NULL && strcmp(argv[1], "-q") == 0) {
-      fd = open("/dev/null", O_WRONLY);
-      close(STDOUT_FILENO);
-      dup2(STDOUT_FILENO, fd);
-    }
-
-    // Prepare the shared memory for io. Clear memory and initialize.
-    int *fd = &virt_quad_io_file_descriptor;
-    *fd = shm_open(VIRT_QUAD_SHARED_MEMORY, O_CREAT | O_RDWR | O_TRUNC, 0666);
-    ftruncate(*fd, sizeof(struct VirtQuadIO));
-    virt_quad_io = mmap(NULL, sizeof(struct VirtQuadIO), PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
-    pthread_mutexattr_t attr;
-    pthread_mutexattr_init(&attr);
-    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
-    pthread_mutex_init(&virt_quad_io->led_lock, &attr);
-    pthread_mutex_init(&virt_quad_io->motors_lock, &attr);
-    pthread_mutex_init(&virt_quad_io->rc_lock, &attr);
+  return 0;
+}
+
+enum CLISubCommand parse_sub_command(char *argv[]) {
+  if (argv[0] == NULL) {
+    return USAGE;
+  }
+  else if (strcmp(argv[0], "start") == 0) {
+    return START;
+  }
+  else if (strcmp(argv[0], "help") == 0) {
+    return HELP;
+  }
+  else if (strcmp(argv[0], "get") == 0 && argv[1] != NULL) {
+    return GET;
+  }
+  else if (strcmp(argv[0], "set") == 0 && argv[1] != NULL && argv[2] != NULL) {
+    return SET;
   }
   else {
-    // parse command line arguments
+    return USAGE;
+  }
+}
 
-    // Open exising shared memory for io. DO NOT CLEAR.
-    int *fd = &virt_quad_io_file_descriptor;
-    *fd = shm_open(VIRT_QUAD_SHARED_MEMORY, O_CREAT | O_RDWR, 0666);
-    virt_quad_io = mmap(NULL, sizeof(struct VirtQuadIO), PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
+int start_virt_quad(char *argv[]) {
+  int fd;
 
-    if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0)  {
-      usage(argv[0]);
-      exit(0);
-    }
-    else if (strcmp(argv[1], "get") == 0 && argv[2] != NULL) {
-      handle_io_output(argv[2]);
-    }
-    else if (strcmp(argv[1], "set") == 0 && argv[2] != NULL && argv[3] != NULL) {
-      handle_io_input(argv[2], argv[3]);
-    }
-    else {
-      puts("Error in parsing commands");
-      usage(argv[0]);
-    }
-    return 0;
+  // Allow making the quad quiet
+  if (argv[0] != NULL && strcmp(argv[0], "-q") == 0) {
+    fd = open("/dev/null", O_WRONLY);
+    close(STDOUT_FILENO);
+    dup2(STDOUT_FILENO, fd);
   }
 
-  puts("Starting the quad application");
+  puts("Starting the virtual quad.");
+  puts("Open another tab to query and control the virt quad, using the get and set commands.");
   quad_main(setup_hardware);
   return 0;
 }
 
+void open_new_shm_io() {
+  // Prepare the shared memory for io. Clear memory and initialize.
+  int *fd = &virt_quad_io_file_descriptor;
+  *fd = shm_open(VIRT_QUAD_SHARED_MEMORY, O_CREAT | O_RDWR | O_TRUNC, 0666);
+  ftruncate(*fd, sizeof(struct VirtQuadIO));
+  virt_quad_io = mmap(NULL, sizeof(struct VirtQuadIO), PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
+  pthread_mutexattr_t attr;
+  pthread_mutexattr_init(&attr);
+  pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+  pthread_mutex_init(&virt_quad_io->led_lock, &attr);
+  pthread_mutex_init(&virt_quad_io->motors_lock, &attr);
+  pthread_mutex_init(&virt_quad_io->rc_lock, &attr);
+}
+
+void open_existing_shm_io() {
+  // Open exising shared memory for io. DO NOT CLEAR.
+  int *fd = &virt_quad_io_file_descriptor;
+  *fd = shm_open(VIRT_QUAD_SHARED_MEMORY, O_CREAT | O_RDWR, 0666);
+  virt_quad_io = mmap(NULL, sizeof(struct VirtQuadIO), PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
+}
+
+
 /**
  * The user wants to read an output by name. Get the output
  * and print to stdout.
@@ -203,25 +246,79 @@ void print_shm_int(int *value, pthread_mutex_t *lock) {
 }
 
 void usage(char *executable_name) {
-  printf("Usage: %s [ -h | --help | -q ] [ command ]\n", executable_name);
+  printf("Usage: %s command [ args ]\n", executable_name);
   puts("Overview:");
   puts("  The virtual quad emulates the behavior of the real quad in the Unix");
   puts("  environment. Start the virtual quad in one tab (no arguments), and");
   puts("  then control the I/O of the quad through commands.");
+  puts("");
+  puts("  For help on a particular command, pass the command name to the help command");
+  puts("  Example:");
+  printf("    %s help get\n", executable_name);
+  puts("");
   puts("Commands:");
-  puts("  get output");
-  puts("  set input value");
-  puts("Outpus:");
-  puts("  led");
-  puts("  motors");
-  puts("Inputs:");
-  puts("  rc_gear");
-  puts("  rc_flap");
-  puts("  rc_throttle");
-  puts("  rc_pitch");
-  puts("  rc_roll");
-  puts("  rc_yaw");
-  puts("Examples:");
-  printf("  %s get led    # prints 0 or 1 to stdout\n", executable_name);
-  printf("  in%s set rc_gear 1    # sets gear to \"on\" \n", executable_name);
+  puts("  help");
+  puts("  get");
+  puts("  set");
+  puts("  start");
+  puts("");
+}
+
+void help_sub_command(char *argv[]) {
+  if (argv[2] == NULL) {
+    usage(argv[0]);
+  }
+  else if (strcmp(argv[2], "help") == 0) {
+    puts("Really?");
+  }
+  else if (strcmp(argv[2], "get") == 0) {
+    printf("Usage: %s get output_name\n", argv[0]);
+    puts("");
+    puts("  Get an output on the virtual quad, specified by its output_name, and print");
+    puts("  to stdout");
+    puts("");
+    puts("  Possible output names");
+    puts("    led");
+    puts("    motor1");
+    puts("    motor2");
+    puts("    motor3");
+    puts("    motor4");
+    puts("");
+  }
+  else if (strcmp(argv[2], "set") == 0) {
+    printf("Usage: %s set input_name val\n", argv[0]);
+    puts("");
+    puts("  Set the value of an input on the quad, specified by its input_name");
+    puts("");
+    puts("  Possible input names");
+    puts("    rc_throttle");
+    puts("    rc_pitch");
+    puts("    rc_roll");
+    puts("    rc_yaw");
+    puts("    rc_gear");
+    puts("    rc_flap");
+    puts("    i2c_imu_x");
+    puts("    i2c_imu_y");
+    puts("    i2c_imu_z");
+    puts("    i2c_imu_p");
+    puts("    i2c_imu_q");
+    puts("    i2c_imu_r");
+    puts("    i2c_imu_mag_x");
+    puts("    i2c_imu_mag_y");
+    puts("    i2c_imu_mag_z");
+    puts("");
+  }
+  else if (strcmp(argv[2], "start") == 0) {
+    printf("Usage: %s start [ -q ]\n", argv[0]);
+    puts("");
+    puts("  Start the virtual quad. Exit using ctrl-c. In order to get and set I/O,");
+    puts("  you'll need to open another tab.");
+    puts("");
+    puts("  Flags");
+    puts("    q - Keep the quad quiet; don't print logs to the stdout");
+    puts("");
+  }
+  else {
+    usage(argv[0]);
+  }
 }
-- 
GitLab