Mid level control code

Dependencies:   ros_lib_kinetic

main.cpp

Committer:
WD40andTape
Date:
2018-08-07
Revision:
9:cd3607ba5643
Parent:
8:d6657767a182
Child:
10:1b6daba32452

File content as of revision 9:cd3607ba5643:

// STANDARD IMPORTS
#include "math.h"
// MBED IMPORTS
#include "mbed.h"
#include "mbed_events.h"
// CUSTOM IMPORTS
#include "HLComms.h"
#include "LLComms.h"

// SIMPLE CHANNEL SELECTION
#define ADC_PRESSURE 1
#define ADC_POSITION 3

#define N_CHANNELS 8 // Number of channels to control
// 1-3: front segment; 4-6: rear segment; 7-8: mid segment

// SPI SETTINGS
#define LOW_LEVEL_SPI_FREQUENCY 10000000
// PATH GENERATION SETTINGS
#define PATH_SAMPLE_TIME_S 0.005 //0.109
#define MAX_LENGTH_MM 100.0
#define MAX_ACTUATOR_LENGTH 52.2
#define MAX_SPEED_MMPS 24.3457
#define PATH_TOLERANCE_MM 0.2 // How close the linear path must get to the target position before considering it a success.

#define BAUD_RATE 9600 //115200

// COMMS SETTINGS
const short int SERVER_PORT = 80;

//const double DBL_MAX_CHAMBER_LENGTHS_MM[N_CHANNELS] = {80.0,80.0,80.0,80.0,80.0,80.0,80.0,80.0};
//const double DBL_ACTUATOR_CONVERSION[N_CHANNELS] = {0.30586,0.30586,0.30586,0.30586,0.30586,0.4588,0.4588}; // Convert from chamber lengths to actuator lengths
const double DBL_ACTUATOR_CONVERSION[N_CHANNELS] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; // Convert from chamber lengths to actuator 
const double DBL_SMOOTHING_FACTOR = 0.5; // 0<x<=1, where 1 is no smoothing
const double DBL_PATH_TOLERANCE = MAX_SPEED_MMPS * PATH_SAMPLE_TIME_S * 1.05; // Path tolerance in mm with 5% tolerance

// PATH VARIABLES
double dblVelocity_mmps[N_CHANNELS] = { 0.0 }; // The linear path velocity (not sent to actuator)
double dblLinearPathCurrentPos_mm[N_CHANNELS]={ 0.0 }; // The current position of the linear path (not sent to actuator)
double dblTargetActPos_mm[N_CHANNELS] = { 0.0 }; // The final target position for the actuator

int intDemandPos_Tx[N_CHANNELS]; //13-bit value to be sent to the actuator

Serial pc(USBTX, USBRX); // tx, rx for usb debugging
LLComms llcomms(LOW_LEVEL_SPI_FREQUENCY);//(N_CHANNELS);

EventQueue queue(32 * EVENTS_EVENT_SIZE);
Thread t(osPriorityRealtime);
Thread threadReceiveAndReplan(osPriorityBelowNormal);
Thread threadSmoothPathPlan(osPriorityNormal);

Mutex mutPathIn;
Semaphore semPathPlan(1);

Timer timer;
Ticker PathCalculationTicker;

bool isDataReady[N_CHANNELS] = { 0 }; // flag to indicate path data is ready for transmission to low level.

void startPathPlan() { // Plan a new linear path after receiving new target data
    semPathPlan.release(); // Uses threadReceiveAndReplan which is below normal priority to ensure consistent transmission to LL
}

// This function will be called when a new transmission is received from high level
void ReceiveAndReplan() {
    HLComms hlcomms(SERVER_PORT);
    
    int error_code;
    error_code = hlcomms.setup_server();
    if( error_code == -1 ) return;
    error_code = hlcomms.accept_connection();
    if( error_code == -1 ) {
        hlcomms.close_server();
        return;
    }
    
    int ii;
    struct msg_format input; //hlcomms.msg_format
    
    while( true ) {
        // RECEIVE MESSAGE
        error_code = hlcomms.receive_message();
        if( error_code == NSAPI_ERROR_NO_CONNECTION ) { // -3004
            printf("Client disconnected.\n\r");
            hlcomms.close_server();
            return;
        } else if( error_code < 0 ) {
            printf("Error %i. Could not send data over the TCP socket. "
                "Perhaps the server socket is not connected to a remote host? "
                "Or the socket is set to non-blocking or timed out?\n\r", error_code);
            hlcomms.close_server();
            return;
        }
        //printf("Message received.\r\n");
        input = hlcomms.process_message();
        
        // PROCESS INPUT
        double dblTargetChambLen_mm[N_CHANNELS]; // The currenly assigned final target position (actuator will reach this at end of path)
        printf("REPLAN, %f\r\n",input.duration);
        // Update front segment
        dblTargetChambLen_mm[0] = input.psi[0][0]*1000;
        dblTargetChambLen_mm[1] = input.psi[0][1]*1000;
        dblTargetChambLen_mm[2] = input.psi[0][2]*1000;
        // Update mid segment
        dblTargetChambLen_mm[6] = input.psi[1][0]*1000;
        dblTargetChambLen_mm[7] = dblTargetChambLen_mm[6]; // Same because two pumps are used
        // Update rear segment
        dblTargetChambLen_mm[3] = input.psi[2][0]*1000;
        dblTargetChambLen_mm[4] = input.psi[2][1]*1000;
        dblTargetChambLen_mm[5] = input.psi[2][2]*1000;

        bool isTimeChanged = 0;
        double dblMaxRecalculatedTime = input.duration;
        mutPathIn.lock(); // Lock variables to avoid race condition
        for (ii = 0; ii< N_CHANNELS; ii++) {
            //check to see if positions are achievable
            /*if(dblTargetChambLen_mm[ii]>DBL_MAX_CHAMBER_LENGTHS_MM[ii]) {
                dblTargetChambLen_mm[ii] = DBL_MAX_CHAMBER_LENGTHS_MM[ii]; 
                isTimeChanged = 1;
            }
            if(dblTargetChambLen_mm[ii]<0.0) {
                dblTargetChambLen_mm[ii] = 0.0;
                isTimeChanged = 1; 
            }*/
            dblTargetActPos_mm[ii] = dblTargetChambLen_mm[ii]*DBL_ACTUATOR_CONVERSION[ii];
            //!! LIMIT CHAMBER LENGTHS TOO
            if( dblTargetActPos_mm[ii]<0.0 || dblTargetActPos_mm[ii]>25.0 ) {
                dblTargetActPos_mm[ii] = min( max( 0.0 , dblTargetActPos_mm[ii] ) , 25.0 );
                isTimeChanged = 1;
            }
        }
        double dblActPosChange;
        short sgn;
        for (ii = 0; ii< N_CHANNELS; ii++) { // Work out new velocities
            dblActPosChange = dblTargetActPos_mm[ii] - dblLinearPathCurrentPos_mm[ii];
            if( fabs(dblActPosChange) < DBL_PATH_TOLERANCE ) {
                dblActPosChange = 0.0;
                isTimeChanged = 1;
            }
            if( input.duration < 0.000000001 ) {
                sgn = (dblActPosChange > 0) ? 1 : ((dblActPosChange < 0) ? -1 : 0);
                dblVelocity_mmps[ii] = sgn * MAX_SPEED_MMPS;
                isTimeChanged = 1;
            } else {
                dblVelocity_mmps[ii] = dblActPosChange / input.duration;
            }
            // Check to see if velocities are achievable
            if(abs(dblVelocity_mmps[ii]) > MAX_SPEED_MMPS) {
                if(dblVelocity_mmps[ii]>0) {
                    dblVelocity_mmps[ii] = MAX_SPEED_MMPS;
                } else {
                    dblVelocity_mmps[ii] = -1*MAX_SPEED_MMPS;
                }
                isTimeChanged = 1;
            }
            double dblRecalculatedTime;
            if( fabs(dblVelocity_mmps[ii]) < 0.000000001 ) {
                dblRecalculatedTime = 0;
            } else {
                dblRecalculatedTime = (dblTargetActPos_mm[ii] - dblLinearPathCurrentPos_mm[ii]) / dblVelocity_mmps[ii];
            }
            if( dblRecalculatedTime > dblMaxRecalculatedTime ) {
                dblMaxRecalculatedTime = dblRecalculatedTime;
            }
        }
        if( isTimeChanged ) {
            if( dblMaxRecalculatedTime < input.duration ) {
                dblMaxRecalculatedTime = input.duration;
            }
            for (ii = 0; ii< N_CHANNELS; ii++) { // Work out new velocities
                dblVelocity_mmps[ii] = (dblTargetActPos_mm[ii] - dblLinearPathCurrentPos_mm[ii]) / dblMaxRecalculatedTime;
            }
        }
        mutPathIn.unlock(); // Unlock mutex
        
        printf("Sending message...\r\n");
        // SEND MESSAGE
        hlcomms.make_message(&dblMaxRecalculatedTime);
        error_code = hlcomms.send_message();
        if( error_code < 0 ) {
            printf("Error %i. Could not send data over the TCP socket. "
                "Perhaps the server socket is not bound or not set to listen for connections? "
                "Or the socket is set to non-blocking or timed out?\n\r", error_code);
            hlcomms.close_server();
            return;
        }
        printf("Message sent.\r\n");
    }

}

void CalculateSmoothPath() {
    int jj;
    double dblMeasuredSampleTime;
    double dblSmoothPathCurrentPos_mm[N_CHANNELS] = { 0.0 }; // The current position of the smooth path (not sent to actuator)
    //double dblPosition_mtrs[N_CHANNELS]; // The actual chamber lengths in meters given as the change in length relative to neutral (should always be >=0)
    //double dblPressure_bar[N_CHANNELS]; // The pressure in a given chamber in bar (1 bar  = 100,000 Pa)
    while(1) {
        semPathPlan.wait();
        
        // If run time is more than 50 us from expected, calculate from measured time step
        if (fabs(PATH_SAMPLE_TIME_S*1000000 - timer.read_us()) > 50) {
            dblMeasuredSampleTime = timer.read(); 
        } else {
            dblMeasuredSampleTime = PATH_SAMPLE_TIME_S;
        }
        timer.reset();
            
        for(jj = 0; jj < N_CHANNELS; jj++) {
            //dblPressure_bar[jj] = ReadADCPressure_bar(jj); // Read pressure from channel
            //dblPosition_mtrs[jj] = ReadADCPosition_mtrs(jj); // Read position from channel
            
            // Calculate next step in linear path
            mutPathIn.lock(); // Lock relevant mutex
            dblLinearPathCurrentPos_mm[jj] = dblLinearPathCurrentPos_mm[jj] + dblVelocity_mmps[jj]*dblMeasuredSampleTime;
            if(dblLinearPathCurrentPos_mm[jj] < 0.0) {
                dblLinearPathCurrentPos_mm[jj] = 0.00;
            }
            // Check tolerance
            if (fabs(dblLinearPathCurrentPos_mm[jj] - dblTargetActPos_mm[jj]) <= DBL_PATH_TOLERANCE) {
                dblVelocity_mmps[jj] = 0.0; // Stop linear path generation when linear path is within tolerance of target position
            }
            mutPathIn.unlock(); // Unlock relevant mutex
            
            // Calculate next step in smooth path
            dblSmoothPathCurrentPos_mm[jj] = DBL_SMOOTHING_FACTOR*dblLinearPathCurrentPos_mm[jj] + (1.0-DBL_SMOOTHING_FACTOR)*dblSmoothPathCurrentPos_mm[jj];
            llcomms.mutChannel[jj].lock(); // MUTEX LOCK
            intDemandPos_Tx[jj] = (int) ((dblSmoothPathCurrentPos_mm[jj]/MAX_ACTUATOR_LENGTH)*8191);// Convert to a 13-bit number
            intDemandPos_Tx[jj] = intDemandPos_Tx[jj] & 0x1FFF; // Ensure number is 13-bit 
            llcomms.mutChannel[jj].unlock(); // MUTEX UNLOCK
          
            isDataReady[jj] = 1;//signal that data ready
        } // end for
        
        //printf("%f, %d\r\n",dblSmoothPathCurrentPos_mm[0], intDemandPos_Tx[0]);
        /*printf("%f, %f, %f, %f, %f, %f, %f, %f\r\n",dblLinearPathCurrentPos_mm[0],dblLinearPathCurrentPos_mm[1],dblLinearPathCurrentPos_mm[2],
            dblLinearPathCurrentPos_mm[3],dblLinearPathCurrentPos_mm[4],dblLinearPathCurrentPos_mm[5],dblLinearPathCurrentPos_mm[6],dblLinearPathCurrentPos_mm[7]);*/
        //printf("%f\r\n",dblLinearPathCurrentPos_mm[0]);
        
    } // end while
}

// NEED TO FIGURE OUT POINTER-TO-MEMBER FUNCTIONS TO MOVE THESE INTO LLComms CLASS
void SendReceiveData(int channel, int _intDemandPos_Tx[], bool (*_isDataReady)[8]) {
    int intPosSPI_Rx[N_CHANNELS]; // 13 bit value received over SPI from the actuator
    
    // Get data from controller
    llcomms.spi.format(16,2);
    llcomms.spi.frequency(LOW_LEVEL_SPI_FREQUENCY);
    llcomms.mutChannel[channel].lock(); // Lock mutex for specific Channel
    *llcomms.cs_LL[channel] = 0; // Select relevant chip
    intPosSPI_Rx[channel] = llcomms.spi.write(_intDemandPos_Tx[channel]); // Transmit & receive
    *llcomms.cs_LL[channel] = 1; // Deselect chip
    *_isDataReady[channel] = 0; // Data no longer ready, i.e. we now require new data
    /*if(channel == 0) {
        intGlobalTest = intPosSPI_Rx[channel];
        dblGlobalTest = ((double) (intPosSPI_Rx[channel])/8191.0*52.2);
    }*/
    
    // Sort out received data
    llcomms.chrErrorFlag[channel] = intPosSPI_Rx[channel]>>13;
    
    intPosSPI_Rx[channel] = intPosSPI_Rx[channel] & 0x1FFF;
    //dblPosition_mtrs[channel] = (double)intPosSPI_Rx[channel]/8191*(MAX_ACTUATOR_LENGTH/DBL_ACTUATOR_CONVERSION[channel])/1000;
    llcomms.mutChannel[channel].unlock();//unlock mutex for specific channel
    //printf("%d, %d\r\n",intPosSPI_Rx[0],_intDemandPos_Tx[0]);
}
// Common rise handler function 
void common_rise_handler(int channel) {
    llcomms.pinCheck = 1;
    if (isDataReady[channel]) { // Check if data is ready for tranmission
        //llcomms.ThreadID[channel] = queue.call(&llcomms.SendReceiveData,channel,intDemandPos_Tx,&isDataReady); // Schedule transmission
        //llcomms.ThreadID[channel] = queue.call(&llcomms.SendReceiveData,channel,intDemandPos_Tx,&isDataReady); // Schedule transmission
        //llcomms.ThreadID[channel] = queue.call(mbed::Callback(&llcomms,&LLComms::SendReceiveData),channel,intDemandPos_Tx,&isDataReady); // Schedule transmission
       // llcomms.ThreadID[channel] = queue.call(mbed::Callback<void()>(&llcomms,&LLComms::SendReceiveData),channel,intDemandPos_Tx,&isDataReady); // Schedule transmission
        //llcomms.ThreadID[channel] = queue.call(&llcomms,*llcomms.SendReceiveData,channel,intDemandPos_Tx,&isDataReady); // Schedule transmission
        llcomms.ThreadID[channel] = queue.call(&SendReceiveData,channel,intDemandPos_Tx,&isDataReady); // Schedule transmission
        //llcomms.ThreadID[channel] = queue.call(&llcomms,&LLComms::dSendReceiveData,channel,intDemandPos_Tx,&isDataReady); // Schedule transmission
        //callback(&low_pass1, &LowPass::step)
    }
}
// Common fall handler functions
void common_fall_handler(int channel) {
    llcomms.pinCheck = 0;
    queue.cancel(llcomms.ThreadID[channel]); // Cancel relevant queued event
}
// Stub rise functions
void rise0(void) { common_rise_handler(0); }
void rise1(void) { common_rise_handler(1); }
void rise2(void) { common_rise_handler(2); }
void rise3(void) { common_rise_handler(3); }
void rise4(void) { common_rise_handler(4); }
void rise5(void) { common_rise_handler(5); }
void rise6(void) { common_rise_handler(6); }
void rise7(void) { common_rise_handler(7); }
// Stub fall functions
void fall0(void) { common_fall_handler(0); }
void fall1(void) { common_fall_handler(1); }
void fall2(void) { common_fall_handler(2); }
void fall3(void) { common_fall_handler(3); }
void fall4(void) { common_fall_handler(4); }
void fall5(void) { common_fall_handler(5); }
void fall6(void) { common_fall_handler(6); }
void fall7(void) { common_fall_handler(7); }

int main() {    
    pc.baud(BAUD_RATE);
    printf("Hi, there! I'll be your mid-level controller for today.\r\n");
    wait(3);
    
    llcomms.reset();
    t.start(callback(&queue, &EventQueue::dispatch_forever)); // Start the event queue
    
    // Set up rise interrupts MIGHT NOT NEED TO BE POINTERS
    llcomms.pinGate0.rise(&rise0);
    llcomms.pinGate1.rise(&rise1);
    llcomms.pinGate2.rise(&rise2);
    llcomms.pinGate3.rise(&rise3);
    llcomms.pinGate4.rise(&rise4);
    llcomms.pinGate5.rise(&rise5);
    llcomms.pinGate6.rise(&rise6);
    llcomms.pinGate7.rise(&rise7);
    // Set up fall interrupts MIGHT NOT NEED TO BE POINTERS
    llcomms.pinGate0.fall(&fall0);
    //llcomms.pinGate0.fall(&llcomms,&LLComms::dfall0);
    llcomms.pinGate1.fall(&fall1);
    llcomms.pinGate2.fall(&fall2);
    llcomms.pinGate3.fall(&fall3);
    llcomms.pinGate4.fall(&fall4);
    llcomms.pinGate5.fall(&fall5);
    llcomms.pinGate6.fall(&fall6);
    llcomms.pinGate7.fall(&fall7);
    
    timer.start();

    threadReceiveAndReplan.start(ReceiveAndReplan);// Start replanning thread
    threadSmoothPathPlan.start(CalculateSmoothPath); // Start planning thread

    PathCalculationTicker.attach(&startPathPlan, PATH_SAMPLE_TIME_S); // Set up planning thread to recur at fixed intervals
    
    Thread::wait(1);
    while(1) {
        Thread::wait(osWaitForever); 
    }
}