From ef646e1667ac92bafea4f88c5944627db0004ea5 Mon Sep 17 00:00:00 2001
From: Brendan Bartels <bbartels@iastate.edu>
Date: Sat, 15 Apr 2017 00:36:33 -0500
Subject: [PATCH] quad: improve virtual quad hardware layer implementation

- use shared memory instead of fifos for most I/O
- re-enable functional tests
---
 quad/Makefile                                 |   2 +-
 quad/scripts/tests/test_safety_checks.rb      |  47 ++++---
 quad/scripts/tests/testing_library.rb         |  64 +++++-----
 quad/src/quad_app/initialize_components.c     |  37 +++---
 quad/src/quad_app/util.c                      |  20 ++-
 quad/src/quad_app/util.h                      |   2 +
 quad/src/virt_quad/Makefile                   |   2 +-
 quad/src/virt_quad/hw_impl_unix.h             |  17 +++
 quad/src/virt_quad/hw_impl_unix_i2c.c         | 107 +---------------
 quad/src/virt_quad/hw_impl_unix_mio7_led.c    |  42 ++-----
 quad/src/virt_quad/hw_impl_unix_motor.c       |  57 +--------
 quad/src/virt_quad/hw_impl_unix_rc_receiver.c |  72 +----------
 quad/src/virt_quad/main.c                     | 117 ++++++++++++++++++
 13 files changed, 248 insertions(+), 338 deletions(-)

diff --git a/quad/Makefile b/quad/Makefile
index dde14bff5..b6838e4bf 100644
--- a/quad/Makefile
+++ b/quad/Makefile
@@ -38,7 +38,7 @@ test: all
 	$(MAKE) -C src/queue test
 	$(MAKE) -C src/computation_graph test
 	$(MAKE) -C src/quad_app test
-	# ruby scripts/tests/test_safety_checks.rb
+	ruby scripts/tests/test_safety_checks.rb
 	# ruby scripts/tests/test_unix_uart.rb
 	# ruby scripts/tests/run_virtual_test_flight.rb
 
diff --git a/quad/scripts/tests/test_safety_checks.rb b/quad/scripts/tests/test_safety_checks.rb
index 9e7d693e6..3e45b244a 100644
--- a/quad/scripts/tests/test_safety_checks.rb
+++ b/quad/scripts/tests/test_safety_checks.rb
@@ -16,15 +16,12 @@ Timeout::timeout(30) {
 
   puts("Setting up...")
 
-
-  sleep 1
-
   # Start virtual quad
   quad_pid = Process.spawn("./virt-quad -q",
                            { :rlimit_as => 536870912, # 512 MiB total RAM
                              :rlimit_stack => 1048576}) # 1 MiB stack
 
-  delay_spin_cursor(5)
+  delay_spin_cursor(1)
 
   #################
   #  Begin Tests
@@ -35,8 +32,11 @@ Timeout::timeout(30) {
     puts("Beginning tests...")
 
     # Set gravity and gear
-    File.write(I2C_MPU_ACCEL_Z, -1 * GRAVITY)
-    File.write(GEAR, GEAR_OFF)
+    set_gear GEAR_OFF
+    set_flap FLAP_ON
+    `./virt-quad set rc_roll 0.498`
+    `./virt-quad set rc_pitch 0.497`
+    `./virt-quad set rc_yaw 0.498`
 
     puts("Check that motors are off at startup")
     check_motors_are_off "Motors weren't off at startup! How dangerous!"
@@ -47,7 +47,7 @@ Timeout::timeout(30) {
     puts("Check that increasing the throttle does nothing to motors")
     # (because gear is still off)
     for i in (THROTTLE_MIN..THROTTLE_MAX).step(0.01)
-      File.write(THROTTLE, i)
+      set_throttle(i)
       check_motors_are_off "Was able to turn on motors with GEAR off! Yikes!"
       check_led(0, "LED turned on while GEAR was off!")
       sleep 0.005
@@ -55,47 +55,44 @@ Timeout::timeout(30) {
 
     puts("Check that flipping gear to 1 while throttle is high does nothing")
     # (motors should still be off, LED should still be off)
-    File.write(GEAR, GEAR_ON)
-    sleep 0.5
+    sleep 0.01
     check_motors_are_off "Motors turned on by gear while rc throttle was high! How dangerous!"
     i = THROTTLE_MAX
     while i > THROTTLE_MID
       i -= 0.01
-      File.write(THROTTLE, i)
+      set_throttle(i)
       check_motors_are_off "Motors turned on while GEAR was off. Yikes!"
       check_led(0, "LED turned on while GEAR was off!")
       sleep 0.005
     end
 
     # (swtich GEAR back to off and bring throttle off)
-    File.write(GEAR, GEAR_OFF)
-    File.write(THROTTLE, THROTTLE_MIN)
+    set_gear GEAR_OFF
+    set_throttle THROTTLE_MIN
 
     puts("Check that the LED turns on when gear is flipped on")
     # (motors should still be off because our throttle is low)
-    File.write(GEAR, GEAR_ON)
-    sleep 0.5
+    set_gear GEAR_ON
+    sleep 0.01
     check_motors_are_off "Motors turned on while throttle was low! Yikes!"
     check_led(1, "LED didn't turn on when GEAR was switched on!")
 
     puts("Check that motors turn on")
-    File.write(THROTTLE, THROTTLE_MID)
-    averages = get_motor_averages
-    average = (averages[0] + averages[1] + averages[2] + averages[3])/4
-    puts averages, "(#{average})"
-    assert average.between?(THROTTLE_EIGHTH, MOTOR_MAX)
+    set_throttle THROTTLE_MID
+    spinner = Thread.new { delay_spin_cursor(5) }
+    motors = get_motor_averages(100, 5)
+    spinner.exit
+    p motors
+    motors.each { |value| assert_operator(value, :>, THROTTLE_EIGHTH) }
 
     puts("Check that gear switch kills the motors")
-    # (and that light goes off)
-    File.write(GEAR, GEAR_OFF)
-    sleep 0.5
+    set_gear GEAR_OFF
+    sleep 0.01
     check_motors_are_off "Motors didn't turn off when GEAR was switched off! How dangerous!"
-    #check_led 0
 
     # (Bring the RC throttle back down)
-    File.write(THROTTLE, THROTTLE_MIN)
+    set_throttle THROTTLE_MIN
 
-    sleep 1
     puts "All safety checks passed."
 
   ensure
diff --git a/quad/scripts/tests/testing_library.rb b/quad/scripts/tests/testing_library.rb
index 5324eacdd..9da10aef8 100644
--- a/quad/scripts/tests/testing_library.rb
+++ b/quad/scripts/tests/testing_library.rb
@@ -9,6 +9,8 @@ MOTOR_MIN = 0.0
 MOTOR_MAX = 1.0
 GEAR_ON = 0.70800
 GEAR_OFF = 0.18300
+FLAP_ON = 0.9
+FLAP_OFF = 0.1
 GRAVITY = 4096
 
 MOTOR1 = "virt-quad-fifos/motor1"
@@ -33,47 +35,41 @@ require 'timeout'
 require 'thread'
 include Test::Unit::Assertions
 
-$fifos = Hash.new
+# Utility functions
+def check_motors_are_off(msg)
+  motors = `./virt-quad get motors`.chomp.split("\n").map { |s| s.to_f }
+  motors.each { |val|
+    assert_operator(val, :<=, THROTTLE_MIN, msg)
+  }
+end
 
-def read_fifo_num(f)
-  if not $fifos.key?(f)
-    $fifos[f] = File.open(f)
-  end
-  $fifos[f].read().chomp.split("\n").last.to_i
+def check_led(is_on, msg)
+  led = `./virt-quad get led`.to_i
+  assert_equal(is_on, led, msg)
 end
 
-# Utility functions
-def check_motors_are_off(msg)
-  motor1 = read_fifo_num(MOTOR1)
-  motor2 = read_fifo_num(MOTOR2)
-  motor3 = read_fifo_num(MOTOR3)
-  motor4 = read_fifo_num(MOTOR4)
-  assert_operator(motor1, :<=, THROTTLE_MIN, msg)
-  assert_operator(motor2, :<=, THROTTLE_MIN, msg)
-  assert_operator(motor3, :<=, THROTTLE_MIN, msg)
-  assert_operator(motor4, :<=, THROTTLE_MIN, msg)
+def set_gear(gear)
+  `./virt-quad set rc_gear #{gear}`
 end
 
-def get_motor_averages
-  motors = [[], [], [], []]
-  for i in 0..100
-    motors[0].push(read_fifo_num(MOTOR1))
-    motors[1].push(read_fifo_num(MOTOR2))
-    motors[2].push(read_fifo_num(MOTOR3))
-    motors[3].push(read_fifo_num(MOTOR4))
-    sleep 0.010
-  end
-  average = []
-  average[0] = motors[0].inject(:+).to_f / motors[0].size
-  average[1] = motors[1].inject(:+).to_f / motors[1].size
-  average[2] = motors[2].inject(:+).to_f / motors[2].size
-  average[3] = motors[3].inject(:+).to_f / motors[3].size
-  average
+def set_flap(flap)
+  `./virt-quad set rc_flap #{flap}`
 end
 
-def check_led(is_on, msg)
-  led = read_fifo_num(LED)
-  assert_equal(is_on, led, msg)
+def set_throttle(throttle)
+  `./virt-quad set rc_throttle #{throttle}`
+end
+
+def get_motor_averages(times, total_time)
+  motor_sums = [0.0, 0.0, 0.0, 0.0]
+  for i in 0..times
+    motors = `./virt-quad get motors`.chomp.split("\n").map { |s| s.to_f }
+    for i in 0..3
+      motor_sums[i] += motors[i]
+    end
+    sleep (total_time.to_f / times.to_f)
+  end
+  motor_sums.map {|val| val / times}
 end
 
 def delay_spin_cursor(delay)
diff --git a/quad/src/quad_app/initialize_components.c b/quad/src/quad_app/initialize_components.c
index 5cd49342d..6e85fa4a1 100644
--- a/quad/src/quad_app/initialize_components.c
+++ b/quad/src/quad_app/initialize_components.c
@@ -15,26 +15,27 @@
 
 extern int Xil_AssertWait;
 
+int start_condition_is_met(float *rc_commands) {
+  return rc_commands[THROTTLE] < 0.125 &&
+    gear_is_engaged(rc_commands[GEAR]) &&
+    flap_is_engaged(rc_commands[FLAP]);
+}
+
 int protection_loops(modular_structs_t *structs)
 {
-	float rc_commands[6]; // 6 "receiver_input" elements: Throttle, Pitch, Roll, Yaw, Gear, and Flap
-	
-	struct RCReceiverDriver *rc_receiver = &structs->hardware_struct.rc_receiver;
-	read_rec_all(rc_receiver, rc_commands);
-	
-	while(rc_commands[THROTTLE] < 0.075 || // wait for RC receiver to connect to transmitter
-        rc_commands[THROTTLE] > 0.125 || // Wait until throttle is low
-        read_kill(rc_commands[GEAR]) || // Wait until gear switch is engaged (so you don't immediately break out of the main loop below)
-        !read_flap(rc_commands[FLAP])) // Wait for the flight mode to be set to manual
-    {
-		process_received(structs);
-		read_rec_all(rc_receiver, rc_commands);
-	}
-
-	// let the pilot/observers know that the quad is now active
-	MIO7_led_on();
-
-	return 0;
+  float rc_commands[6];
+  struct RCReceiverDriver *rc_receiver = &structs->hardware_struct.rc_receiver;
+  read_rec_all(rc_receiver, rc_commands);
+
+  while(!start_condition_is_met(rc_commands)) {
+    process_received(structs);
+    read_rec_all(rc_receiver, rc_commands);
+  }
+
+  // let the pilot/observers know that the quad is now active
+  MIO7_led_on();
+
+  return 0;
 }
 
 int init_structs(modular_structs_t *structs) {
diff --git a/quad/src/quad_app/util.c b/quad/src/quad_app/util.c
index e982432ea..698d453c7 100644
--- a/quad/src/quad_app/util.c
+++ b/quad/src/quad_app/util.c
@@ -49,15 +49,23 @@ int hexStrToInt(char *buf, int startIdx, int endIdx) {
  * Otherwise, do nothing
  */
 int read_kill(float gear) {
-	if(gear < GEAR_MID)
-		return 1;
-	return 0;
+  if (gear_is_engaged(gear)) return 0;
+  else return 1;
+}
+
+int gear_is_engaged(float gear) {
+  if (gear > GEAR_MID) return 1;
+  else return 0;
 }
 
 int read_flap(float flap) {
-	if(flap > FLAP_MID)
-		return 1;
-	return 0;
+  if (flap_is_engaged(flap)) return 1;
+  else return 0;
+}
+
+int flap_is_engaged(float flap) {
+  if (flap > FLAP_MID) return 1;
+  else return 0;
 }
 
 /**
diff --git a/quad/src/quad_app/util.h b/quad/src/quad_app/util.h
index 390bafd9d..d67542829 100644
--- a/quad/src/quad_app/util.h
+++ b/quad/src/quad_app/util.h
@@ -14,6 +14,8 @@
 void read_rec_all(struct RCReceiverDriver *rc_receiver, float *mixer);
 int read_kill(float gear);
 int read_flap(float flap);
+int gear_is_engaged(float gear);
+int flap_is_engaged(float flap);
 void kill_motors(struct MotorDriver *motors);
 
 int build_int(u8 *buff);
diff --git a/quad/src/virt_quad/Makefile b/quad/src/virt_quad/Makefile
index acf6d27cb..241f5e48b 100644
--- a/quad/src/virt_quad/Makefile
+++ b/quad/src/virt_quad/Makefile
@@ -1,7 +1,7 @@
 TOP=../..
 
 NAME = virt-quad
-REQLIBS = -Wl,--whole-archive -lquad_app -Wl,--no-whole-archive -lcommands -lgraph_blocks -lcomputation_graph -lm -lpthread
+REQLIBS = -Wl,--whole-archive -lquad_app -Wl,--no-whole-archive -lcommands -lgraph_blocks -lcomputation_graph -lm -lpthread -lrt
 
 include $(TOP)/executable.mk
 
diff --git a/quad/src/virt_quad/hw_impl_unix.h b/quad/src/virt_quad/hw_impl_unix.h
index 85e223f28..296d30fbf 100644
--- a/quad/src/virt_quad/hw_impl_unix.h
+++ b/quad/src/virt_quad/hw_impl_unix.h
@@ -9,9 +9,26 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "controllers.h"
+#include <pthread.h>
+#include <errno.h>
 
 #define VIRT_QUAD_FIFOS_DIR "virt-quad-fifos"
 
+struct VirtQuadIO {
+  pthread_mutex_t led_lock;
+  int led;
+  pthread_mutex_t motors_lock;
+  float motors[4];
+  pthread_mutex_t rc_lock;
+  float rc_receiver[6];
+};
+
+#define VIRT_QUAD_SHARED_MEMORY "/virt-quad-io"
+
 int unix_uart_reset(struct UARTDriver *self);
 int unix_uart_write(struct UARTDriver *self, unsigned char c);
 int unix_uart_read(struct UARTDriver *self, unsigned char *c);
diff --git a/quad/src/virt_quad/hw_impl_unix_i2c.c b/quad/src/virt_quad/hw_impl_unix_i2c.c
index 97228f0ba..aa2c1d5ce 100644
--- a/quad/src/virt_quad/hw_impl_unix_i2c.c
+++ b/quad/src/virt_quad/hw_impl_unix_i2c.c
@@ -7,49 +7,7 @@
 
 #define NUM_INPUTS 7
 
-void * update_i2c_input_cache(void *);
-
-union val {
-  unsigned char b[2];
-  unsigned short s;
-};
-
-static char *input_names[NUM_INPUTS];
-static int fifos[NUM_INPUTS];
-static union val cache[NUM_INPUTS];
-
-static short last_dev;
-static short last_reg;
-static short last_val;
-
-static int nums[] = {0, 1, 2, 3, 4, 5};
-static pthread_t workers[NUM_INPUTS];
-
 int unix_i2c_reset(struct I2CDriver *self) {
-  input_names[0] = "i2c-mpu-accel-x";
-  input_names[1] = "i2c-mpu-accel-y";
-  input_names[2] = "i2c-mpu-accel-z";
-  input_names[3] = "i2c-mpu-gryo-x";
-  input_names[4] = "i2c-mpu-gryo-y";
-  input_names[5] = "i2c-mpu-gyro-z";
-  input_names[6] = "i2c-lidar";
-
-  mkdir(VIRT_QUAD_FIFOS_DIR, 0777);
-
-  // Start up worker thread whose job is to update the caches
-  int i;
-  for (i = 0; i < NUM_INPUTS; i += 1) {
-    pthread_create(&workers[i], 0, update_i2c_input_cache, &nums[i]);
-  }
-
-  cache[0].s = 0;
-  cache[1].s = 0;
-  cache[2].s = 0;
-  cache[3].s = 0;
-  cache[4].s = 0;
-  cache[5].s = 0;
-  cache[6].s = 0;
-
   return 0;
 }
 
@@ -57,73 +15,12 @@ int unix_i2c_write(struct I2CDriver *self,
                    unsigned short device_addr,
                    unsigned char *data,
                    unsigned int length) {
-  if (length == 2) {
-    last_dev = device_addr;
-    last_reg = data[0];
-    last_val = data[1];
-  }
-  return 0;
+  return -1;
 }
 
 int unix_i2c_read(struct I2CDriver *self,
                   unsigned short device_addr,
                   unsigned char *buff,
                   unsigned int length) {
-  if (last_dev != device_addr) {
-    return -1;
-  }
-
-  switch (device_addr) {
-  case MPU9150_DEVICE_ADDR:
-    if (last_reg == ACCEL_GYRO_BASE_ADDR) {
-      buff[0] = cache[0].b[0];
-      buff[1] = cache[0].b[1];
-      buff[2] = cache[1].b[0];
-      buff[3] = cache[1].b[1];
-      buff[4] = cache[2].b[0];
-      buff[5] = cache[2].b[1];
-      buff[6] = 0;
-      buff[7] = 0;
-      buff[8] = cache[3].b[0];
-      buff[9] = cache[3].b[1];
-      buff[10] = cache[4].b[0];
-      buff[11] = cache[4].b[1];
-      buff[12] = cache[5].b[0];
-      buff[13] = cache[5].b[1];
-    }
-    else if (last_reg == LIDARLITE_DEVICE_ADDR) {
-      buff[0] = cache[6].b[0];
-      buff[1] = cache[6].b[0];
-    }
-    else {
-      return -1;
-    }
-  }
-  return 0;
-}
-
-void * update_i2c_input_cache(void *arg) {
-  int *cache_index = arg;
-  int i = *cache_index;
-  char buff[16];
-
-  // Setup FIFO
-  unlink(input_names[i]);
-  char fifoname[64];
-  sprintf(fifoname, "%s/%s", VIRT_QUAD_FIFOS_DIR, input_names[i]);
-  mkfifo(fifoname, 0666);
-  fifos[i] = open(fifoname, O_RDONLY);
-
-  // Block while waiting for reads
-  while (1) {
-    int bytes_read = read(fifos[i], buff, 15);
-    if (bytes_read > 0) {
-      buff[bytes_read] = '\0';
-      unsigned long val = strtoll(buff, NULL, 10);
-      cache[i].s = val;
-      printf("%s: %ld\n", input_names[i], val);
-    }
-    pthread_yield();
-  }
-  return NULL;
+  return -1;
 }
diff --git a/quad/src/virt_quad/hw_impl_unix_mio7_led.c b/quad/src/virt_quad/hw_impl_unix_mio7_led.c
index b9b66dd09..af831c580 100644
--- a/quad/src/virt_quad/hw_impl_unix_mio7_led.c
+++ b/quad/src/virt_quad/hw_impl_unix_mio7_led.c
@@ -7,25 +7,10 @@
 #include <unistd.h>
 #include <pthread.h>
 
-void * output_cached_led();
-
-int on;
-static char *led_fifo_name;
-pthread_t worker;
+extern struct VirtQuadIO *virt_quad_io;
+static int on;
 
 int unix_mio7_led_reset(struct LEDDriver *self) {
-  led_fifo_name = VIRT_QUAD_FIFOS_DIR "/mio7-led";
-
-  mkdir(VIRT_QUAD_FIFOS_DIR, 0777);
-  int i;
-  for (i = 0; i < 4; i += 1) {
-    unlink(led_fifo_name);
-    mkfifo(led_fifo_name, 0666);
-  }
-
-  // Start up worker thread whose job is to update the caches
-  pthread_create(&worker, 0, output_cached_led, NULL);
-
   return 0;
 }
 
@@ -33,6 +18,11 @@ int unix_mio7_led_turn_on(struct LEDDriver *self) {
   if (!on) {
     puts("LED ON");
     on = 1;
+
+    struct VirtQuadIO *io = virt_quad_io;
+    pthread_mutex_lock(&io->led_lock);
+    io->led = on;
+    pthread_mutex_unlock(&io->led_lock);
   }
   return 0;
 }
@@ -41,17 +31,11 @@ int unix_mio7_led_turn_off(struct LEDDriver *self) {
   if (on) {
     puts("LED OFF");
     on = 0;
-  }
-  return 0;
-}
 
-void * output_cached_led() {
-  char buff[16];
-  while (1) {
-    int fifo = open(led_fifo_name, O_WRONLY);
-    sprintf(buff, "%d\n", on);
-    write(fifo, buff, strlen(buff));
-    close(fifo);
-    pthread_yield();
-  }
+    struct VirtQuadIO *io = virt_quad_io;
+    pthread_mutex_lock(&io->led_lock);
+    io->led = on;
+    pthread_mutex_unlock(&io->led_lock);
+}
+  return 0;
 }
diff --git a/quad/src/virt_quad/hw_impl_unix_motor.c b/quad/src/virt_quad/hw_impl_unix_motor.c
index 3ad271b52..98da452a0 100644
--- a/quad/src/virt_quad/hw_impl_unix_motor.c
+++ b/quad/src/virt_quad/hw_impl_unix_motor.c
@@ -6,65 +6,18 @@
 #include <pthread.h>
 #include <unistd.h>
 
-void * output_cache();
-
-static char *output_pwms[4];
-static float cache[4];
-pthread_t worker;
-static void float_equals(float a, float b);
-
-static int zero = 0;
-static int one = 1;
-static int two = 2;
-static int three = 3;
+extern struct VirtQuadIO *virt_quad_io;
 
 int unix_motor_reset(struct MotorDriver *self) {
-  output_pwms[0] = VIRT_QUAD_FIFOS_DIR "/motor1";
-  output_pwms[1] = VIRT_QUAD_FIFOS_DIR "/motor2";
-  output_pwms[2] = VIRT_QUAD_FIFOS_DIR "/motor3";
-  output_pwms[3] = VIRT_QUAD_FIFOS_DIR "/motor4";
-
-  mkdir(VIRT_QUAD_FIFOS_DIR, 0777);
-
-  // Start up worker thread whose job is to update the caches
-  pthread_create(&worker, 0, output_cache, &zero);
-  pthread_create(&worker, 0, output_cache, &one);
-  pthread_create(&worker, 0, output_cache, &two);
-  pthread_create(&worker, 0, output_cache, &three);
-
   return 0;
 }
 
 int unix_motor_write(struct MotorDriver *self,
                           unsigned int channel,
                           float magnitude) {
-  if (cache[channel] != magnitude) {
-    printf("%s: %.2f\n", output_pwms[channel], magnitude);
-  }
-  cache[channel] = magnitude;
+  struct VirtQuadIO *io = virt_quad_io;
+  pthread_mutex_lock(&io->motors_lock);
+  io->motors[channel] = magnitude;
+  pthread_mutex_unlock(&io->motors_lock);
   return 0;
 }
-
-void * output_cache(void *arg) {
-  int *output_index = arg;
-  char buff[16];
-  int i = *output_index;
-
-  // Setup FIFO
-  unlink(output_pwms[i]);
-  mkfifo(output_pwms[i], 0666);
-
-  // Block while waiting for someone to listen
-  while (1) {
-    int fifo = open(output_pwms[i], O_WRONLY);
-    sprintf(buff, "%.2f\n", cache[i]);
-    write(fifo, buff, strlen(buff));
-    close(fifo);
-    pthread_yield();
-  }
-  return NULL;
-}
-
-static void float_equals(float a, float b) {
-  return fabs(a - b) < 0.00001;
-}
diff --git a/quad/src/virt_quad/hw_impl_unix_rc_receiver.c b/quad/src/virt_quad/hw_impl_unix_rc_receiver.c
index 229a019cd..85cf852c7 100644
--- a/quad/src/virt_quad/hw_impl_unix_rc_receiver.c
+++ b/quad/src/virt_quad/hw_impl_unix_rc_receiver.c
@@ -5,80 +5,18 @@
 #include <fcntl.h>
 #include <pthread.h>
 
-void * update_input_cache();
-
-static char *input_names[6];
-static int fifos[6];
-static float cache[6];
-static pthread_t workers[6];
-static int nums[] = {0, 1, 2, 3, 4, 5};
+extern struct VirtQuadIO *virt_quad_io;
 
 int unix_rc_receiver_reset(struct RCReceiverDriver *self) {
-  input_names[0] = "rc-throttle";
-  input_names[1] = "rc-roll";
-  input_names[2] = "rc-pitch";
-  input_names[3] = "rc-yaw";
-  input_names[4] = "rc-gear";
-  input_names[5] = "rc-flap";
-
-  mkdir(VIRT_QUAD_FIFOS_DIR, 0777);
-
-  // Start up worker thread whose job is to update the caches
-  int i;
-  for (i = 0; i < 6; i += 1) {
-    pthread_create(&workers[i], 0, update_input_cache, &nums[i]);
-    usleep(1000);
-  }
-
-  cache[0] = THROTTLE_MIN;
-  cache[1] = ROLL_CENTER;
-  cache[2] = PITCH_CENTER;
-  cache[3] = YAW_CENTER;
-  cache[4] = GEAR_0;
-  cache[5] = FLAP_1;
-
-  for (i = 0; i < 6; i += 1) {
-    printf("%s: %.2f\n", input_names[i], cache[i]);
-  }
-
   return 0;
 }
 
 int unix_rc_receiver_read(struct RCReceiverDriver *self,
                         unsigned int channel,
                         float *magnitude) {
-
-  *magnitude = cache[channel];
+  struct VirtQuadIO *io = virt_quad_io;
+  pthread_mutex_lock(&io->rc_lock);
+  *magnitude = io->rc_receiver[channel];
+  pthread_mutex_unlock(&io->rc_lock);
   return 0;
 }
-
-void * update_input_cache(void *arg) {
-  int *cache_index = arg;
-  int i = *cache_index;
-  char buff[16];
-
-  // Setup FIFO
-  unlink(input_names[i]);
-  char fifoname[64];
-  sprintf(fifoname, "%s/%s", VIRT_QUAD_FIFOS_DIR, input_names[i]);
-  mkfifo(fifoname, 0666);
-  fifos[i] = open(fifoname, O_RDONLY);
-
-  // Block while waiting for reads
-  while (1) {
-    int bytes_read = read(fifos[i], buff, 15);
-    if (bytes_read > 0) {
-      buff[bytes_read] = '\0';
-      float val = strtof(buff, NULL);
-      if (val <= 1 && val >= 0) {
-	cache[i] = val;
-	printf("%s: %.2f\n", input_names[i], val);
-      }
-      else {
-	printf("%s: Bad value (%f) - input not received\n", input_names[i], val);
-      }
-    }
-    pthread_yield();
-  }
-  return NULL;
-}
diff --git a/quad/src/virt_quad/main.c b/quad/src/virt_quad/main.c
index 5f6aaf8db..7f9e4447e 100644
--- a/quad/src/virt_quad/main.c
+++ b/quad/src/virt_quad/main.c
@@ -4,6 +4,13 @@
 #include "quad_app.h"
 #include <fcntl.h>
 
+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);
+
+struct VirtQuadIO *virt_quad_io;
+int virt_quad_io_file_descriptor;
+
 int setup_hardware(hardware_t *hardware) {
   hardware->i2c = create_unix_i2c();
   hardware->rc_receiver = create_unix_rc_receiver();
@@ -30,7 +37,117 @@ int main(int argc, char *argv[]) {
       exit(EXIT_FAILURE);
     }
   }
+  argc -= optind;
+  argv += optind;
+
+
+  // Decide if we are launching the quad or responding to a request
+  // about a running quad
+  if (argv[0] == NULL) {
+    // Prepare the shared memory for io. Clear memory and initialize.
+    puts("Setting up...");
+    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);
+    puts("Finished setting up.");
+  }
+  else {
+    // 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);
+
+    // Meet requests
+    if (strcmp(argv[0], "get") == 0 && argv[1] != NULL) {
+      handle_io_output(argv[1]);
+    }
+    else if (strcmp(argv[0], "set") == 0 && argv[1] != NULL && argv[2] != NULL) {
+      handle_io_input(argv[1], argv[2]);
+    }
+    return 0;
+  }
 
+  puts("Starting the quad application");
   quad_main(setup_hardware);
   return 0;
 }
+
+/**
+ * The user wants to read an output by name. Get the output
+ * and print to stdout.
+ */
+int handle_io_output(const char *name) {
+  int ret = 0;
+  struct VirtQuadIO *io = virt_quad_io;
+  if (strcmp(name, "led") == 0) {
+    pthread_mutex_lock(&io->led_lock);
+    printf("%d\n", io->led);
+    pthread_mutex_unlock(&io->led_lock);
+  }
+  else if (strcmp(name, "motors") == 0) {
+    pthread_mutex_lock(&io->motors_lock);
+    int i;
+    for (i = 0; i < 4; i += 1) {
+      printf("%f\n", io->motors[i]);
+    }
+    pthread_mutex_unlock(&io->motors_lock);
+  }
+  else {
+    puts("Given output not recognized.");
+    ret = -1;
+  }
+  return ret;
+}
+
+/**
+ * The user wants to set an input by name, and has supplied
+ * a value. Set the appropriate input to the given value.
+ */
+int handle_io_input(const char *name, const char *value_str) {
+  int ret = 0;
+  errno = 0;
+  float value = strtof(value_str, NULL);
+  if (errno) {
+    ret = -1;
+    puts("Given number format was bad.");
+  }
+  else {
+    struct VirtQuadIO *io = virt_quad_io;
+    if (strcmp(name, "rc_throttle") == 0) {
+      set_shm(value, &io->rc_receiver[THROTTLE], &io->rc_lock);
+    }
+    else if (strcmp(name, "rc_roll") == 0) {
+      set_shm(value, &io->rc_receiver[ROLL], &io->rc_lock);
+    }
+    else if (strcmp(name, "rc_pitch") == 0) {
+      set_shm(value, &io->rc_receiver[PITCH], &io->rc_lock);
+    }
+    else if (strcmp(name, "rc_yaw") == 0) {
+      set_shm(value, &io->rc_receiver[YAW], &io->rc_lock);
+    }
+    else if (strcmp(name, "rc_gear") == 0) {
+      set_shm(value, &io->rc_receiver[GEAR], &io->rc_lock);
+    }
+    else if (strcmp(name, "rc_flap") == 0) {
+      set_shm(value, &io->rc_receiver[FLAP], &io->rc_lock);
+    }
+    else {
+      puts("Given input not recognized.");
+      ret = -1;
+    }
+  }
+  return ret;
+}
+
+void set_shm(float value, float *dest, pthread_mutex_t *lock) {
+  pthread_mutex_lock(lock);
+  *dest = value;
+  pthread_mutex_unlock(lock);
+}
-- 
GitLab