Mid level control code

Dependencies:   ros_lib_kinetic

main.cpp

Committer:
WD40andTape
Date:
2019-07-09
Revision:
36:4459be8296e9
Parent:
34:54e9ebe9e87f

File content as of revision 36:4459be8296e9:

// 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"

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

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

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();
        hlcomms.send_sensor_message(llcomms.positionSensor_uint,llcomms.pressureSensor_uint);
    }
}

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

// This function will be called when a new transmission is received from high level
void ReceiveAndReplan() {
    
    SendSensorDataTicker.attach(&signalSendSensorData, 1/(float)SENSOR_FEEDBACK_HZ); // Set up planning thread to recur at fixed intervals
    
    struct demands_struct input;
    DigitalOut SupportPins[4] = {PE_4, PE_2, PE_3, PE_6};
    
    while( true ) {
        hlcomms.newData.wait();
        input = hlcomms.get_demands();
        // SUPPORT FUNCTIONS
        // [isInsufflate,isSuction,isWashLens,isJet]
        for(short int i=0; i<4; i++) {
            if(i<2) { // Active low, i.e. 0 = Off
                SupportPins[i].write((short int)input.utitilies_bool[i]);
            } else { // Active high, i.e. 1 = Off
                SupportPins[i].write((short int)(!input.utitilies_bool[i]));
            }
        }
        // PROCESS INPUT
        double maxTimesToTarget_s[3] = { -1.0 };
        //[8,7,6,4,3,-1,-1,0,-1]
        //FRONT = channels 0,1,2
        //MID = channels 3,4(,5)
        //REAR = channels (6,)7(,8)
        // Lock mutex, preventing setDemandsForLL from running
        mutPathIn.lock();
        for(short int segNum=0; segNum<3; segNum++) {
            // Limit requested speed
            if( input.speeds_mmps[segNum] > 0 ) {
                double limitedSpeed_mmps = min( max( 0.0 , input.speeds_mmps[segNum] ) , (double)MAX_SPEED_MMPS );
                // For each actuator, limit the input position, calculate the position change, and select the absolute max
                double dblDistanceToTarget_mm[3] = { -1.0 };
                double maxDistanceToTarget_mm = 0.0;
                for(short int i=0; i<3; i++) {
                    short int channel = segNum*3+i;
                    if(channel>=N_CHANNELS) {
                        continue;
                    }
                    double dblCurrentPosition_mm = llcomms.positionSensor_mm[channel];
                    if(input.psi_mm[channel]<0 || dblCurrentPosition_mm<0) { // If requested position is negative or the sensor feedback is erroneous
                        continue;
                    }
                    // Limit actuator position
                    input.psi_mm[channel] = min( max( 0.0 , input.psi_mm[channel] ) , (double)MAX_ACTUATOR_LIMIT_MM );
                    // Calculate actuator position change
                    dblDistanceToTarget_mm[i] = fabs(input.psi_mm[channel] - dblCurrentPosition_mm);
                    // Select the max absolute actuator position change
                    if(dblDistanceToTarget_mm[i]>maxDistanceToTarget_mm) {
                        maxDistanceToTarget_mm = dblDistanceToTarget_mm[i];
                    }
                }
                // For max actuator position change, calculate the time to destination at the limited speed
                maxTimesToTarget_s[segNum] = fabs(maxDistanceToTarget_mm) / limitedSpeed_mmps;
                // For each actuator, replan target position and velocity as required
                for(short int i=0; i<3; i++) {
                    short int channel = segNum*3+i;
                    // If requested actuator position change is already within tolerance, do NOT replan that actuator
                    if( dblDistanceToTarget_mm[i] <= 0.0 ) continue;
                    // Calculate velocity for each motor to synchronise movements to complete in max time
                    // Set dblDemandPosition_mm and dblDemandSpeed_mmps
                    dblDemandPosition_mm[channel] = input.psi_mm[channel];
                    dblDemandSpeed_mmps[channel] = dblDistanceToTarget_mm[i] / maxTimesToTarget_s[segNum];
                }
            } else {
                maxTimesToTarget_s[segNum] = -1.0;
            }
        }
        // Unlock mutex, allowing setDemandsForLL to run again
        mutPathIn.unlock();
        
        // SEND MESSAGE
        hlcomms.send_durations_message(maxTimesToTarget_s);
    }

}

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

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_mm[i] = dblDemandPosition_mm[i];
            llcomms.demandSpeed_mmps[i] = dblDemandSpeed_mmps[i];
            llcomms.mutChannel[i].unlock(); // MUTEX UNLOCK
            llcomms.isDataReady[i] = 1; // Signal that data ready
        } // end for
        mutPathIn.unlock(); // Unlock relevant mutex
    } // end while(1)

}

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/(float)LL_DEMANDS_FREQ_HZ); // Set up LL comms thread to recur at fixed intervals
    
    Thread::wait(1);
    while(1) {
        Thread::wait(osWaitForever); 
    }
}