Basic Mid-Level control for the rebuilt MorphGI control unit, using PWM to communicate with the low level controllers.

Dependencies:   ros_lib_kinetic

Revision:
6:f0a18e28a322
Parent:
5:712e7634c779
Child:
7:5b6a2cefbf3b
--- a/main.cpp	Wed Aug 01 09:51:23 2018 +0000
+++ b/main.cpp	Fri Aug 03 11:08:12 2018 +0000
@@ -1,8 +1,40 @@
 #include "mbed.h"
 #include "math.h"
+#include "mbed_events.h"
 
+// COMMS IMPORTS
+#include <sstream> // stringstream
+#include <vector> // vector
+#include <string.h> // strtok
 //#include "mbed.h"
-#include "mbed_events.h"
+#include "EthernetInterface.h"
+#include "TCPServer.h"
+#include "TCPSocket.h"
+
+// COMMS SETTINGS
+const bool IS_DHCP = false;
+#define PORT 80
+
+// COMMS STRUCT DECLARATIONS
+struct msg_format {
+    double psi[3][3];
+    double duration;
+};
+struct comms_interfaces {
+    EthernetInterface eth; // Indicates which NetworkInterface the socket should be created on
+    TCPServer srv; // Provides the ability to accept incoming TCP connections
+    TCPSocket clt_sock; // Provides the ability to send a stream of data over TCP
+    SocketAddress clt_addr; // Represents the IP address and port pair of a unique network endpoint
+};
+
+// COMMS FUNCTION DECLARATIONS
+int setup_server(void);
+int accept_connection(void);
+void close_server(void);
+msg_format consume_message( unsigned char * msg );
+
+// COMMS GLOBAL VARIABLES
+struct comms_interfaces comms;
 
 //ADC SPI stuff
 #define PREAMBLE 0x06
@@ -26,17 +58,18 @@
 //parameters to manually change                       
 #define LOW_LEVEL_SPI_FREQUENCY 10000000
 
-#define PATH_SAMPLE_TIME_S 0.005
+#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.
 
-const double DBL_MAX_CHAMBER_LENGTHS_MM[N_CHANNELS] = {500.0,50.0,50.0,50.0,50.0,50.0,30.0,30.0};
-const double DBL_ACTUATOR_CONVERSION[N_CHANNELS] = {0.24176,0.24176,0.24176,0.24176,0.24176,0.36264,0.36264};// covert from chamber lengths to actuator lengths
+//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};// covert 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 = 0.2;
+const double DBL_PATH_TOLERANCE = MAX_SPEED_MMPS * PATH_SAMPLE_TIME_S * 1.05; //path tolerance in mm with 5% tolerance
 //-----These are the variables being shared between High-Level and Mid-Level-----------------------------------------------//
 
 //
@@ -246,13 +279,13 @@
 {
     int ii;
 
-    double dblPSI[3][3]; //the message from high-level containing the chamber lengths in meters in following format:
+    //double dblPSI[3][3]; //the message from high-level containing the chamber lengths in meters in following format:
             /*
             {L(front,1),   L(front,2),    L(front,3);
              L(mid,1)  ,   L(mid,2)  ,    L(mid,3);
              L(rear,1) ,   L(rear,2) ,    L(rear,3);}
             */
-    double dblTargetTime_s; //the time in seconds desired to reach the target (>0...!)
+    //double dblTargetTime_s; //the time in seconds desired to reach the target (>0...!)
     //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).    
 
@@ -261,66 +294,143 @@
     
     //receive
     
-    double dblTargetChambLen_mm[N_CHANNELS]; //the currenly assigned final target position (actuator will reach this at end of path)
+    // Error codes: https://github.com/ARMmbed/mbed-os/blob/master/features/netsocket/nsapi_types.h
+    // http://man7.org/linux/man-pages/man3/errno.3.html
+    
+    int error_code;
+    
+    error_code = setup_server();
+    if( error_code == -1 ) return;
+    error_code = accept_connection();
+    if( error_code == -1 ) return;
+    
+    unsigned char recv_buffer[1024];
+    struct msg_format input;
+    char send_buffer[65];
+    char * message_to_send;
     
-    bool kk = 0;
-    while(1) {
+    while( true ) {
+        // RECEIVE MESSAGE
+        memset(recv_buffer, 0, sizeof(recv_buffer));
+        error_code = comms.clt_sock.recv(recv_buffer, 1024); // Blocks until data is received
+        if( error_code == NSAPI_ERROR_NO_CONNECTION ) { // -3004
+            printf("Client disconnected.\n\r");
+            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);
+            close_server();
+            return;
+        }
+        //printf("Message received.\r\n");
+        input = consume_message( recv_buffer );
+        //!! Set global variables to input with mutex
         
-        if(kk == 0) {
-            dblPSI[0][0] = (double) 0.20;
-            dblTargetTime_s = 3.0;
-        } else { 
-            dblPSI[0][0] = (double) 0.0; 
-            dblTargetTime_s = 3.0;
-        }
-        kk = !kk;
-        printf("REPLAN, %f,\r\n", dblPSI[0][0] );
+        // 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] = dblPSI[0][0]*1000;
-        dblTargetChambLen_mm[1] = dblPSI[0][1]*1000;
-        dblTargetChambLen_mm[2] = dblPSI[0][2]*1000;
+        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] = dblPSI[1][0]*1000;
+        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] = dblPSI[2][0]*1000;
-        dblTargetChambLen_mm[4] = dblPSI[2][1]*1000;
-        dblTargetChambLen_mm[5] = dblPSI[2][2]*1000;
+        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]) {
+            /*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; 
+                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;
             }
-            
-            mutPathIn.lock();//lock variables to avoid race condition
-                dblTargetActPos_mm[ii] = dblTargetChambLen_mm[ii]*DBL_ACTUATOR_CONVERSION[ii];
-                
+            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]) / dblTargetTime_s;
-                
-                //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;
-                    }
-                }
-            mutPathIn.unlock(); //unlock mutex.
+                dblVelocity_mmps[ii] = (dblTargetActPos_mm[ii] - dblLinearPathCurrentPos_mm[ii]) / dblMaxRecalculatedTime;
+            }
+        }
             
             
-            //dblPressure_bar[ii] = 0.5;//read pressure from channel
-//            dblPosition_mtrs[ii] = 0.02; //read position from channel
-            
-            //send info back to HL
-        } // end of for
+        //dblPressure_bar[ii] = 0.5;//read pressure from channel
+        //dblPosition_mtrs[ii] = 0.02; //read position from channel
         //printf("%f\r\n", dblVelocity_mmps[0]);
-        Thread::wait(7000);
-    } // end of while(1)    
+        mutPathIn.unlock(); //unlock mutex.
+        
+        printf("Sending message...\r\n");
+        // SEND MESSAGE
+        memset(send_buffer, 0, sizeof(send_buffer));
+        _snprintf(send_buffer,64,"%f",dblMaxRecalculatedTime);
+        message_to_send = send_buffer;
+        error_code = comms.clt_sock.send(message_to_send, strlen(message_to_send));
+        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);
+            close_server();
+            return;
+        }
+        printf("Message sent.\r\n");
+    }
+
 }
 
 // lock mutex
@@ -337,7 +447,6 @@
     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();
         
@@ -362,6 +471,10 @@
                 if(dblLinearPathCurrentPos_mm[jj] < 0.0) {
                     dblLinearPathCurrentPos_mm[jj] = 0.00;
                 }
+                /*if(dblLinearPathCurrentPos_mm[jj] > 0.0) {
+                    dblLinearPathCurrentPos_mm[jj] = 0.00;
+                }*/
+                
                 
                 //check tolerance
                 if (fabs(dblLinearPathCurrentPos_mm[jj] - dblTargetActPos_mm[jj]) <= DBL_PATH_TOLERANCE) {
@@ -381,10 +494,10 @@
             isDataReady[jj] = 1;//signal that data ready
 
         } // end for
-        //printf("%f, %d\r\n",dblSmoothPathCurrentPos_mm[0], intDemandPos_Tx[0]); 
-        mutChannel[0].lock();
-        printf("%f, %f, %f\r\n",dblSmoothPathCurrentPos_mm[0],dblPressure_bar[0], dblPosition_mtrs[0]);
-        mutChannel[0].unlock();
+        //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
 }
 
@@ -409,7 +522,7 @@
     }
     
     //calculate some control variables
-    //dblPathTolerance = 0.1;//MAX_SPEED_MMPS * PATH_SAMPLE_TIME_S * 1.05; //path tolerance in mm. 
+    
     
     pinReset = 1; //initialise reset pin to not reset the controllers.
     wait(0.25);
@@ -418,7 +531,7 @@
     pinReset = 1;//ready to go
     
     //say something nice to the user.
-    pc.baud(115200);
+    pc.baud(9600); //115200
     printf("Hi, there! I'll be your mid-level controller for today.\r\n");
     wait(3);
     // Start the event queue
@@ -477,3 +590,111 @@
     }
 }
 
+
+int setup_server(void) {
+    int error_code;
+    printf("Starting TCP server...\n\r");
+    if( !IS_DHCP ) {
+        const char* ip = "10.101.79.135"; //"10.101.81.125"; // Get first 3 parts from "ifconfig" on PC
+        const char* mask = "255.255.255.0"; // Get from "ifconfig" on PC
+        const char* gateway =  "10.101.79.1"; //"10.101.81.1"; // Get from "route -n" on PC
+        error_code = comms.eth.set_network(ip, mask, gateway); // Dont use DHCP
+        if( error_code < 0 ) {
+            printf("Error %i. Could not network interface to use a static IP address. "
+                "Perhaps the network is already connected?\n\r", error_code);
+            return -1;
+        }
+    }
+    error_code = comms.eth.connect();
+    if( error_code < 0 ) {
+        printf("Error %i. Could not start the Ethernet interface.\n\r", error_code);
+        return -1;
+    }
+    const char* server_ip = comms.eth.get_ip_address();
+    if( server_ip == NULL ) {
+       error_code = -1;
+        printf("Error %i. Ethernet interface is not yet connected.\n\r", error_code);
+        return -1;
+    }
+    printf("The target IP address is '%s'\n\r", server_ip);
+    error_code = comms.srv.open(&comms.eth); // Open the server on ethernet stack
+    if( error_code < 0 ) {
+        printf("Error %i. Could not open server socket.\n\r", error_code);
+        return -1;
+    }
+    error_code = comms.srv.bind(comms.eth.get_ip_address(), PORT); // Bind the HTTP port (TCP 80) to the server
+    if( error_code < 0 ) {
+        printf("Error %i. Could not bind server socket to port '%d'.\n\r", error_code, PORT);
+        return -1;
+    }
+    error_code = comms.srv.listen(1); // Listen for 1 simultaneous connection
+    if( error_code < 0 ) {
+        printf("Error %i. Could not put server socket into listening mode.\n\r", error_code);
+        return -1;
+    }
+    return 0;
+}
+
+
+int accept_connection(void) {
+    printf("Waiting to accept a connection on the TCP socket.\n\r");
+    int error_code;
+    error_code = comms.srv.accept(&comms.clt_sock, &comms.clt_addr); // Blocks until data is sent
+    if( error_code < 0 ) {
+        printf("Error %i. Could not create a network socket using the specified socket instance. "
+            "Perhaps the socket is set to non-blocking or timed out?\n\r", error_code);
+        return -1;
+    }
+    printf("Accepted %s:%d\n\r", comms.clt_addr.get_ip_address(), comms.clt_addr.get_port());
+    return 0;
+}
+
+
+void close_server(void) {
+    comms.clt_sock.close();
+    comms.srv.close();
+    comms.eth.disconnect();
+    return;
+}
+
+
+msg_format consume_message( unsigned char * msg ) {
+    // Break message string into chunks at each comma
+    vector<string> tokens;
+    char * delim = ",";
+    char * token = strtok((char *)msg, delim);
+    while(token != NULL) {
+        tokens.push_back(string(token));
+        //printf("%s\n\r",token);
+        token = strtok(NULL, delim);
+    }
+    // Cast into doubles and assign to variables
+    struct msg_format input;
+    stringstream(tokens[0]) >> input.psi[0][0];
+    stringstream(tokens[1]) >> input.psi[0][1];
+    stringstream(tokens[2]) >> input.psi[0][2];
+    stringstream(tokens[3]) >> input.psi[1][0];
+    stringstream(tokens[4]) >> input.psi[1][1];
+    stringstream(tokens[5]) >> input.psi[1][2];
+    stringstream(tokens[6]) >> input.psi[2][0];
+    stringstream(tokens[7]) >> input.psi[2][1];
+    stringstream(tokens[8]) >> input.psi[2][2];
+    stringstream(tokens[9]) >> input.duration;
+    
+    if(input.psi[0][0] < 0.0) input.psi[0][0] = 0.0;
+    if(input.psi[0][1] < 0.0) input.psi[0][1] = 0.0;
+    if(input.psi[0][2] < 0.0) input.psi[0][2] = 0.0;
+    if(input.psi[1][0] < 0.0) input.psi[1][0] = 0.0;
+    if(input.psi[1][1] < 0.0) input.psi[1][1] = 0.0;
+    if(input.psi[1][2] < 0.0) input.psi[1][2] = 0.0;
+    if(input.psi[2][0] < 0.0) input.psi[2][0] = 0.0;
+    if(input.psi[2][1] < 0.0) input.psi[2][1] = 0.0;
+    if(input.psi[2][2] < 0.0) input.psi[2][2] = 0.0;
+    if(input.duration < 0.0) input.duration = 0.0;
+    /*if( input.psi[2][0] > 0.14 ) {
+        printf("Hi\n\r");
+    }*/
+    //throw std::invalid_argument( "received negative value" );
+    //return 0;
+    return input;
+} // End of consume_message()
\ No newline at end of file