Mid level control code

Dependencies:   ros_lib_kinetic

main.cpp

Committer:
dofydoink
Date:
2018-12-14
Revision:
23:61526647cc8a
Parent:
22:82871f00f89d
Child:
24:bc852aa89e7a

File content as of revision 23:61526647cc8a:

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

//!!!!!!!!!!!!!!!!!!!! CHECK POSITION SENSOR IS FROM 0 AND IN METERS

// Maximum achievable mm path tolerance plus additional % tolerance
const float FLT_PATH_TOLERANCE = MAX_SPEED_MMPS * (1/LL_DEMANDS_FREQ_HZ) * (1.0f+FLT_PERCENT_PATH_TOLERANCE);

// DEMAND VARIABLES
double dblDemandVelocity_mmps[N_CHANNELS] = { 0.0 }; // The linear path velocity (not sent to actuator)
double dblDemandPosition_mtrs[N_CHANNELS] = { 0.0 }; // The final target position for the actuator

Serial pc(USBTX, USBRX); // tx, rx for usb debugging
LLComms llcomms;
HLComms hlcomms(SERVER_PORT);

Thread threadLowLevelSPI(osPriorityRealtime);
Thread threadSetDemands(osPriorityNormal);
Thread threadReceiveAndReplan(osPriorityBelowNormal);
Thread threadSensorFeedback(osPriorityBelowNormal);

Mutex mutPathIn;
Semaphore semLLcomms(1);
Semaphore semSensorData(1);

Ticker setDemandsTicker;
Ticker SendSensorDataTicker;


void sendSensorData() {
    while( true ) {
        semSensorData.wait();
        int error_code = hlcomms.send_sensor_message(llcomms.positionSensor_m,llcomms.pressureSensor_bar);
        /*if( error_code < 0 ) {
            if(IS_PRINT_OUTPUT) 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;
        }*/
    }
}

void signalSendSensorData() {
    semSensorData.release();
}

void startLLcomms() { // Send new demands to LL after receiving new target data
    semLLcomms.release(); // Uses threadSetDemands which is normal priority
}

// This function will be called when a new transmission is received from high level
void ReceiveAndReplan() {
    
    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;
    }
    
    SendSensorDataTicker.attach(&signalSendSensorData, 1/(float)SENSOR_FEEDBACK_HZ); // Set up planning thread to recur at fixed intervals
    
    struct msg_format input; //hlcomms.msg_format
    
    while( true ) {
        // RECEIVE MESSAGE
        error_code = hlcomms.receive_message();
        if( error_code == NSAPI_ERROR_NO_CONNECTION ) { // -3004
            if(IS_PRINT_OUTPUT) printf("Client disconnected.\n\r");
            hlcomms.close_server();
            return;
        } else if( error_code < 0 ) {
            if(IS_PRINT_OUTPUT) 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;
        }
        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! RECV MUTEX
        input = hlcomms.process_message();
        
        // PROCESS INPUT
        if(IS_PRINT_OUTPUT) printf("REPLAN, %f\r\n",input.speed);
        
        double dblTarget_mm[N_CHANNELS]; // The currenly assigned final target position (actuator will reach this at end of path)
        // Update rear segment
        dblTarget_mm[3] = input.psi[0][0]*1000;
        dblTarget_mm[5] = 0.0; //input.psi[0][1]*1000;
        dblTarget_mm[6] = 0.0; //input.psi[0][2]*1000;
        // Update mid segment
        dblTarget_mm[4] = input.psi[1][0]*1000;
        dblTarget_mm[7] = dblTarget_mm[4]; // Same because two pumps are used
        // Update front segment
        dblTarget_mm[0] = input.psi[2][0]*1000;
        dblTarget_mm[1] = input.psi[2][1]*1000;
        dblTarget_mm[2] = input.psi[2][2]*1000;

        // Lock mutex, preventing setDemandsForLL from running
        mutPathIn.lock();        
        // Limit requested speed
        double limitedSpeed_mmps = min( max( 0.0 , input.speed ) , (double)MAX_SPEED_MMPS );
        // For each actuator, limit the input position, calculate the position change, and select the absolute max
        double dblDisplacementToTarget_mm[N_CHANNELS];
        double maxDistanceToTarget_mm = 0.0;
        for(int i=0; i<N_CHANNELS; i++) {
            // If requested position is negative
            if(dblTarget_mm[i]<0) {
                // Set actuator position change to 0
                dblDisplacementToTarget_mm[i] = 0.0;
            } else { // Requested position is positive
                // ? Limit requested chamber lengths
                // ? Convert from chamber length to actuator space
                // Limit actuator position
                dblTarget_mm[i] = min( max( 0.0 , dblTarget_mm[i] ) , (double)MAX_ACTUATOR_LIMIT ); // !!! USE A CONSTANT FOR THIS
                // Calculate actuator position change
                double dblCurrentPosition = llcomms.positionSensor_m[i];
                dblDisplacementToTarget_mm[i] = dblTarget_mm[i] - dblCurrentPosition;
                // Select the max absolute actuator position change
                if(fabs(dblDisplacementToTarget_mm[i])>maxDistanceToTarget_mm) {
                    maxDistanceToTarget_mm = fabs(dblDisplacementToTarget_mm[i]);
                }
            }
        }
        // For max actuator position change, calculate the time to destination at the limited speed
        double maxTimeToTarget_s = fabs(maxDistanceToTarget_mm) / limitedSpeed_mmps;
        // For each actuator, replan target position and velocity as required
        for(int i=0; i<N_CHANNELS; i++) {
            // If requested actuator position change is already within tolerance, do NOT replan that actuator
            if( fabs(dblDisplacementToTarget_mm[i]) < FLT_PATH_TOLERANCE ) continue;
            // Calculate velocity for each motor to synchronise movements to complete in max time
            // Set dblDemandPosition_mtrs and dblDemandVelocity_mmps
            dblDemandPosition_mtrs[i] = dblTarget_mm[i];
            dblDemandVelocity_mmps[i] = dblDisplacementToTarget_mm[i] / maxTimeToTarget_s;
        }
        // Unlock mutex, allowing setDemandsForLL to run again
        mutPathIn.unlock();
        
        // SEND MESSAGE
        error_code = hlcomms.send_duration_message(&maxTimeToTarget_s);
        if( error_code < 0 ) {
            if(IS_PRINT_OUTPUT) 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;
        }
    }

}

void setDemandsForLL() {
    
    while(1) {
        semLLcomms.wait();
        
        mutPathIn.lock(); // Lock relevant mutex
        for(short int i=0; i<N_CHANNELS; i++) { // For each LL
            llcomms.mutChannel[i].lock(); // MUTEX LOCK
            llcomms.demandPosition[i] = (int) ((dblDemandPosition_mtrs[i]/MAX_ACTUATOR_LENGTH)*511); // Convert to a 9-bit number
            llcomms.demandPosition[i] = llcomms.demandPosition[i] & 0x1FF; // Ensure number is 9-bit
            llcomms.demandSpeed[i] = (int) ((dblDemandVelocity_mmps[i]/MAX_SPEED_MMPS)*511);// Convert to a 9-bit number
            llcomms.demandSpeed[i] = llcomms.demandSpeed[i] & 0x1FF; // Ensure number is 9-bit
            llcomms.mutChannel[i].unlock(); // MUTEX UNLOCK
            llcomms.isDataReady[i] = 1; // Signal that data ready
        } // end for
        mutPathIn.unlock(); // Unlock relevant mutex

    } // end while
        
}

int main() {
    pc.baud(BAUD_RATE);
    printf("ML engage. Compiled at %s\r\n.",__TIME__);
    wait(3);
    
    threadLowLevelSPI.start(callback(&llcomms.queue, &EventQueue::dispatch_forever)); // Start the event queue
    threadReceiveAndReplan.start(ReceiveAndReplan);// Start replanning thread
    threadSetDemands.start(setDemandsForLL); // Start planning thread
    threadSensorFeedback.start(sendSensorData); // Start sensor feedback thread

    setDemandsTicker.attach(&startLLcomms, 1/LL_DEMANDS_FREQ_HZ); // Set up LL comms thread to recur at fixed intervals
    
    Thread::wait(1);
    while(1) {
        Thread::wait(osWaitForever); 
    }
}