/****************************************************************************/
/* Copyright (c) 2015 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  : Power Buoy Data Logger CANBus instrument reader class         */
/* Filename : CanBus.hpp                                                    */
/* Author   : Henthorn                                                      */
/* Project  : Power Buoy                                                    */
/* Version  : 1.0                                                           */
/* Created  : 04/20/2015                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
/*                                                                          */
/* This class is the data source reader for the CANBus instruments on the   */
/* Power Buoy. It is derived from the generic FileSource class.             */
/*                                                                          */
/****************************************************************************/

#ifndef _CANBUS_
#define _CANBUS_

#include <linux/can.h>
#include <linux/can/raw.h>

#include "FileSource.hpp"

class CSVWriter;

class CanBus : public FileSource
{

public:

   // The data contained in a Power Controller CanBUS frame
   //
   struct PC_Record {
      //float  can_time;
      short  seq_num;
      float  rpm;
      float  sd_rpm;
      float  voltage;
      float  power;
      float  bcurrent;
      float  lcurrent;
      float  torque;
      float  diff_press;
      float  bridgedc;
      float  loaddc;
      float  scale;
      float  retract;
      float  target_v;
      unsigned short  status;
      float  sd_rpm_target;
   };

   // The data contained in a Battery Controller CanBUS frame
   //
   struct BC_Record {
      //float  can_time;
      short  seq_num;
      float  voltage;
      float  ips;
      float  vbalance;
      float  vstopcharge;
      float  gfault;
      float  hydrogen;
      unsigned short  status;
   };

#if 0
Packet 0:  (unchanged)
LoadCell  :  Rangefinder : UpperSpringPressure : Lower Spring Pressure

Scaling as before:

Packet 1:  (entirely changed)
epoch most sig. word:  epoch least sig word :  salinity most sig. word :  salinity least significant word

When re-assembled, the epoch seconds is a long that is seconds since 1970 UTC and can just be logged as that.
When re-assembled, the salinity is an unsigned long that is salinity in S/m multiplied by 1000000.  i.e. micro Siemens/m

Packet 2: (only first integer is changed)
temperature  :  Status  :  0  :   0

Here, temperature is an integer representing milli-degrees C.  i.e.  Divide by 1000 to get degrees C.

#endif

   // The data contained in a Spring Controller CanBUS frame
   //
   struct SC_Record {
      //float  can_time;
      short  seq_num;
      short  load;
      float  range;
      float  upsi;
      float  lpsi;
      unsigned long epoch;
      float  salinity;
      float  temperature;
      short  status;
   };

   // The data contained in a Trefoil controller CanBUS frame
   //
   struct TF_Record {
      //float          can_time;
      short          seq_num;
      short          spare1;
      float          tvoltage;
      float          bvoltage;
      float          pressure;
      float          qtn[4];
      float          mag[3];      // gauss
      unsigned short status;      // bit field
      float          ang_rate[3]; // rad/sec
      unsigned short vpe_status;  // bit field
      float          accel[3];    // m/sec^2
      short          spare2;
      unsigned short mstatus;     // Maxon status
      short          mcurr;       // motor current
      int            encoder;     // 32-bit encoder position
   };

   // The data contained in a Rudder Controller CanBUS frame
   //
   struct RC_Record {
      //float          can_time;
      short          seq_num;
      float          x_vel;       // m/s
      float          y_vel;
      float          z_vel;
      float          pressure;    // psi
      char           b_amp1;      //counts
      char           b_amp2;
      char           b_amp3;
      char           b_cor1;      // percentage
      char           b_cor2;
      char           b_cor3;
      float          target;      // heading data (-180 to +180)
      float          measured;
      float          filtered;
      short          rudder;      // counts
      float          ram;         // inches
      float          qtn[4];
      float          mag[3];      // gauss
      unsigned short status;      // bit field
      float          ang_rate[3]; // rad/sec
      unsigned short vpe_status;  // bit field
      float          accel[3];    // m/sec^2
      short          position;    // Rudder position
      float          p_gain;
      float          i_gain;
      float          d_gain;
      float          period;      // update period
      unsigned short ftc;         // Filter time constant
   };



   // Open a connection to the data source.
   // If the pointer to the writer object is NULL
   // no data is written.
   // 
   CanBus(const char* cansock, CSVWriter* w);

   ~CanBus();

   // Return 0 on error
   // Return the (0 - SourceID) if a full record was read from that source
   // Otherwise, return the number of bytes still needed for
   // a full record.
   // 
   // int v = read_data(0);
   // if (v > 0)
   //   v == Data_ID from that source (e.g., PC_0)
   // else
   //   v < 0, error
   //
   int read_data(char verbose);

   // Return data records.
   // 
   // By default - when only a pointer to a record and no SourceID is
   // specified - the freshest complete record is returned (e.g., 
   // get_record(&batt_record) returns the latest complete battery
   // record).
   // 
   // Return 1 if successful, else 0
   // 
   char get_record(PC_Record *pc, SourceID id = PowerConID);
   char get_record(BC_Record *bc, SourceID id = BatteryConID);
   char get_record(SC_Record *sc, SourceID id = SpringConID);
   char get_record(TF_Record *tf, SourceID id = TrefoilConID);
   char get_record(RC_Record *rc, SourceID id = RudderConID);

protected:

   unsigned create_mask(unsigned a, unsigned b);
   void make_masks() {
      _seq_mask  = create_mask(0,8);
      _data_mask = create_mask(9,13);
      _src_mask  = create_mask(14,17);
   }

   void decode_header(struct can_frame *cf);

   // Handle Power Controller frame
   //
   int handle_power_msg(struct can_frame);

   // Handle Pneumatic Spring Controller frame
   //
   int handle_spring_msg(struct can_frame);

   // Handle Battery Controller frame
   //
   int handle_battery_msg(struct can_frame);

   // Handle Trefoil Controller frame
   //
   int handle_trefoil_msg(struct can_frame);

   // Handle Rudder Controller frame
   //
   int handle_rudder_msg(struct can_frame);

   char* _cansock;   // e.g., "can0", "can1"

   unsigned _seq_mask, _data_mask, _src_mask;

   // Double-buffered data record.
   // Use _p to point to the published record buffer
   // _pc_record[_p] is the current stable record
   // _pc_record[1-_p] is the record that is in flux
   // 
   struct CanBus::PC_Record  _pc_record[2];
   struct CanBus::BC_Record  _bc_record[2];
   struct CanBus::SC_Record  _sc_record[2];
   struct CanBus::TF_Record  _tf_record[2];
   struct CanBus::RC_Record  _rc_record[2];
   char   _p, _b, _s, _r, _t;    // Value will always be either 0 or 1

   CSVWriter* _logger;

   unsigned int _src_id, _data_id, _seq_num;

   char _verbose;
};

#endif
