/****************************************************************************/
/* Copyright (c) 2015 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  : Command interface to the CANBus power buoy controllers.       */
/* Filename : pbcmd.c                                                       */
/* Author   : Henthorn                                                      */
/* Project  : Power Buoy                                                    */
/* Version  : 1.0                                                           */
/* Created  : 04/10/2015                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
/*                                                                          */
/* This application is the entry point for many (if not all) the Power Buoy */
/* controller commands. The design is similar to 'busybox' where argv[0]    */
/* determines what command is executed. Soft-links to this program are used */
/* for each supported command. To illustrate:                               */
/*  $                                                                       */
/*  $ ls -l  ~/bin                                                          */
/*  -rwxr-xr-x 1 henthorn dialout 12107 Apr 8 17:26 pbcmd                   */
/*  lrwxrwxrwx 1 henthorn dialout    11 Apr 9 09:52 bender -> pbcmd         */
/*  $                                                                       */
/*  $ bender on   # runs pbcmd with argv[0] == "bender"                     */
/*                                                                          */
/* Issue command 'pbcmd' for help on the supported commands                 */
/*  $                                                                       */
/*  $ pbcmd                                                                 */
/*                                                                          */
/****************************************************************************/

#include "pbcmd.h"

#define  CAN0      "can0"

// Structure used by main to dispatch commands.
// When adding a command, declare it in pbcmd.h,
// implement it in cmd_code.c, and add
// an entry to the command array below.
//
struct PB_CMD {
   char* name;            // command line name (i.e., "bender")
   char* desc;            // short description ("Bends the battery controller")
   int (*f)(int, char**);
};

struct PB_CMD pb_cmds[] = {
   {        "bender", "Sets the state of the bender module", bender},
   { "reset_battery", "Reset battery controller", reset_bc},

   {          "pump", "Spring Controller pump on of off", pump},
   {         "valve", "Spring Controller valve on time or off", valve},
   {  "reset_spring", "Reset Spring Controller", reset_sc},

   {         "scale", "Set the power controller scale", scale},
   {       "retract", "Set the power controller retract", retract},
   {          "vmax", "Set the power controller max voltage", vmax},
   {          "imax", "Set the power controller max current", imax},
   {           "bus", "Set the power controller bus on or off", bus},
   {          "gain", "Set the power controller GS gain", gain},
   {        "target", "Set the power controller GS target", target},
   {"stopchargemode", "Set the stop charge mode option on or off", scm},
   {   "reset_power", "Reset Power Controller", reset_pc},

   {        "rcmode", "Set Rudder Controller Mode", rcmode},
   {         "rcpos", "Set Rudder Position", rcpos},
   {      "rctarget", "Set Rudder Heading Target", rctarget},
   {         "rchdg", "Set Default Rudder Heading", rchdg},
   {      "rcfilter", "Set Rudder Heading Filter TC", rcfilter},
   {      "rcperiod", "Set Rudder Controller Update Period", rcperiod},
   {       "rcpgain", "Set Rudder Controller Proportional Gain", rcpgain},
   {       "rcigain", "Set Rudder Controller Integral Gain", rcigain},
   {       "rcdgain", "Set Rudder Controller Derivative Gain", rcdgain},
   {      "rcmaxpos", "Set Rudder Controller max rudder position", rcmaxpos},
   {    "rcdeadband", "Set Rudder Controller deadband", rcdeadband},
   {  "reset_rudder", "Reset Rudder Controller", rcreset},

   //{      "hv", "Battery Controller high voltage on or off", hv}, // removed
   // {"next", "Next command example", next},

   {NULL, NULL, NULL}   // always the last one
};


void pbcmd_usage()
{
   printf("pbcmd: Multi-call command Power Buoy dispatcher\n"
          "Supported commands:\n");
   int i = 0;
   while(pb_cmds[i].name != NULL)
   {
      printf("* %14s  - %s\n", pb_cmds[i].name, pb_cmds[i].desc);
      i++;
   }
   printf("\nFor help on a command use the command name, i.e., \"bender\"\n");
   printf("\nExcept the reset commands which take no arguments.\n");
   printf("\nDO NOT enter reset_power and expect to get help. The command will execute!\n");
}

int main(int argc, char **argv)
{
   // Finally, execute the command function
   //

   int i = 0;
   while(pb_cmds[i].name != NULL)
   {
      if (!strcmp(argv[0], pb_cmds[i].name))
      {
         printf("Executing %s...\n", pb_cmds[i].name);
         return (pb_cmds[i].f)(argc, argv);
      }

      i++;
   }

   // If execution reaches this point then a sort of version skew has occurred
   // 
   pbcmd_usage();
   return 0;
}

// Return a socket fd setup for CANBus comms.
// Returns BAD_SOCK on failure.
//
int open_cansock(const char *port)
{
   struct ifreq ifr;
   struct sockaddr_can addr;
   int cansock;

   // Get a CAN_RAW socket
   //
   if ( (cansock = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0)
   {
      perror("pbcmd: Unable to acquire socket");
      return BAD_SOCK;
   }

   // Get appropriate ioctl() for socket
   //
   addr.can_family = AF_CAN;
   strcpy(ifr.ifr_name, port);
   if (ioctl(cansock, SIOCGIFINDEX, &ifr) < 0)
   {
      perror("pbcmd: socket ioctl() failed");
      return BAD_SOCK;
   }
   addr.can_ifindex = ifr.ifr_ifindex;

   // Set 
   fcntl(cansock, F_SETFL, O_NONBLOCK);
   if (bind(cansock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
   {
      return BAD_SOCK;
   }

   return cansock;
}

// General purpose functions for use by specific commands
//
int send_frame(struct can_frame *frame)
{
   int cansock = open_cansock(CAN0);

   int retval = write(cansock, frame, sizeof(struct can_frame));
   close(cansock);
   if (retval != sizeof(struct can_frame))
   {
      return BAD_SOCK;
   }
   else
   {
      return OK;
   }
}

