Skip to content
Snippets Groups Projects
main.c 9.21 KiB
#include <stdio.h>
#include <unistd.h>
#include "hw_impl_unix.h"
#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);
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.
 */
int setup_hardware(hardware_t *hardware) {
  hardware->i2c_0 = create_unix_i2c();
  hardware->i2c_1 = create_unix_i2c();
  hardware->rc_receiver = create_unix_rc_receiver();
  hardware->motors = create_unix_motors();
  hardware->uart_0 = create_unix_uart();
  hardware->comm = create_unix_comm(&hardware->uart_0);
  hardware->global_timer = create_unix_global_timer();
  hardware->axi_timer = create_unix_axi_timer();
  hardware->mio7_led = create_unix_mio7_led();
  hardware->sys = create_unix_system();
  hardware->imu = create_unix_imu(&hardware->i2c_0);
  hardware->lidar = create_unix_lidar(&hardware->i2c_1);
  hardware->of = create_unix_optical_flow(&hardware->i2c_0);
  return 0;
}

int main(int argc, char *argv[]) {
  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;
  }

  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 {
    return USAGE;
  }
}

int start_virt_quad(char *argv[]) {
  int fd;

  // 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 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);
  pthread_mutex_init(&virt_quad_io->i2c_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.
 */
int handle_io_output(const char *name) {
  int ret = 0;
  struct VirtQuadIO *io = virt_quad_io;
  if (strcmp(name, "led") == 0) {
    print_shm_int(&io->led, &io->led_lock);
  }
  else if (strcmp(name, "motor1") == 0) {
    print_shm_float(&io->motors[0], &io->motors_lock);
  }
  else if (strcmp(name, "motor2") == 0) {
    print_shm_float(&io->motors[1], &io->motors_lock);
  }
  else if (strcmp(name, "motor3") == 0) {
    print_shm_float(&io->motors[2], &io->motors_lock);
  }
  else if (strcmp(name, "motor4") == 0) {
    print_shm_float(&io->motors[3], &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 if (strcmp(name, "i2c_imu_x") == 0) {
      set_shm(value, &io->gam.accel_x, &io->i2c_lock);
    }
    else if (strcmp(name, "i2c_imu_y") == 0) {
      set_shm(value, &io->gam.accel_y, &io->i2c_lock);
    }
    else if (strcmp(name, "i2c_imu_z") == 0) {
      set_shm(value, &io->gam.accel_z, &io->i2c_lock);
    }
    else if (strcmp(name, "i2c_imu_p") == 0) {
      set_shm(value, &io->gam.gyro_xVel_p, &io->i2c_lock);
    }
    else if (strcmp(name, "i2c_imu_q") == 0) {
      set_shm(value, &io->gam.gyro_yVel_q, &io->i2c_lock);
    }
    else if (strcmp(name, "i2c_imu_r") == 0) {
      set_shm(value, &io->gam.gyro_zVel_r, &io->i2c_lock);
    }
    else if (strcmp(name, "i2c_imu_mag_x") == 0) {
      set_shm(value, &io->gam.mag_x, &io->i2c_lock);
    }
    else if (strcmp(name, "i2c_imu_mag_y") == 0) {
      set_shm(value, &io->gam.mag_y, &io->i2c_lock);
    }
    else if (strcmp(name, "i2c_imu_mag_z") == 0) {
      set_shm(value, &io->gam.mag_z, &io->i2c_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);
}

void print_shm_float(float *value, pthread_mutex_t *lock) {
  pthread_mutex_lock(lock);
  printf("%f\n", *value);
  pthread_mutex_unlock(lock);
}

void print_shm_int(int *value, pthread_mutex_t *lock) {
  pthread_mutex_lock(lock);
  printf("%d\n", *value);
  pthread_mutex_unlock(lock);
}

void usage(char *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, and then control the I/O");
  puts("  of the virtual 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("  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]);
  }
}