John Mitchell / lpc4088_displaymodule_GC500_2_5inch

Dependencies:   DMBasicGUI DMSupport

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers EthernetHandler.cpp Source File

EthernetHandler.cpp

00001 #include "EthernetHandler.h"
00002 
00003 #define DO_USB_IN_THIS_THREAD // If #define'd, we do our own USB comms to the GC here in this thread.
00004                               // If not, we signal the main thread to do it
00005                               
00006 
00007 #define USE_LED_FOR_DEBUGGING
00008 
00009 #ifdef USE_LED_FOR_DEBUGGING
00010 
00011 #include "gpio_api.h"
00012 #include "wait_api.h"
00013 #include "toolchain.h"
00014 #include "mbed_interface.h"
00015 
00016 // We turn on LED 1 when we have received, from the client, a command to send to the GC.
00017 // We turn it off when we send the response back to the client.
00018 static void SetLed1(bool turnLedOn)
00019 {
00020     gpio_t led_1; gpio_init_out(&led_1, LED1);
00021 
00022     if(turnLedOn) {
00023         gpio_write(&led_1, 0); // zero appears to mean "turn LED 1 on"
00024     } else {
00025         gpio_write(&led_1, 1); // one appears to turn it off
00026     }
00027 }
00028 
00029 #endif // USE_LED_FOR_DEBUGGING
00030 
00031 
00032 #include "GuiLib.h"
00033 /*
00034     Displays the specified text string at the specified location in the currently-displayed easyGUI page.
00035     
00036     Defined in main.cpp - and omits the 'ALLOW_DEBUG_PRINTS' #define
00037 */
00038 extern void SpecialDebugPrint(char *stuffToPrint, GuiConst_INT16S X, GuiConst_INT16S Y);
00039 
00040 /*
00041     Tells the caller whether or not a specified GC command represents the start of a method,
00042     and that therefore a new method is now being sent to the GC.
00043     
00044     Params: pointer to a null-terminated string containing the command in question
00045     
00046     Returns true if the command is one that occurs at the start of a method (and nowhere else),
00047     false if not.
00048     
00049     Defined in main.cpp
00050 */
00051 bool IsStartOfMethodCommand(char *gcCommand);
00052 
00053 /*
00054     Tells the caller whether or not a specified GC command represents the end of a method,
00055     and that therefore a new method has just been sent to the GC.
00056     
00057     Params: pointer to a null-terminated string containing the command in question
00058     
00059     Returns true if the command is one that occurs at the end of a method (and nowhere else),
00060     false if not.
00061     
00062     Defined in main.cpp
00063 */
00064 bool IsEndOfMethodCommand(char *gcCommand);
00065 
00066 /*
00067     Tells the caller whether or not a specified GC command is a control command,
00068     which received "DACK" in acknowledgement, and is therefore likely 
00069     to have caused the GC to change its state.
00070     
00071     Params: pointer to a null-terminated string containing the command in question
00072     
00073     Returns true if the command is a control command and if it succeeded, false if not.
00074     
00075     Defined in main.cpp
00076 */
00077 bool IsSuccessfulControlCommand(char *gcCommand, char *gcResponse);
00078 
00079 
00080 osThreadId EthernetHandler::mainThreadId = 0;
00081 bool EthernetHandler::mainThreadIdSet = false;
00082 
00083 TCPSocketServer* EthernetHandler::ethernetServer = NULL;
00084 
00085 USBDeviceConnected* EthernetHandler::usbDevice = NULL;
00086 USBHostGC* EthernetHandler::usbHostGC = NULL;
00087 
00088 char EthernetHandler::gcCommand[20] = "";
00089 char EthernetHandler::gcResponse[20] = "";
00090     
00091 Timer EthernetHandler::timer;
00092 
00093 bool EthernetHandler::receivedGCCommand = false;
00094 
00095 // This is the thread signal value we tell the main thread to use to tell us that it has received a response to the latest command.
00096 // We increment this each time we pass a new Ethernet command to the main thread, so that we know which command the response refers to.
00097 int EthernetHandler::responseReadyThreadSignalCode = GC_RESPONSE_READY; 
00098 
00099 // The maximum possible value for 'responseReadyThreadSignalCode' above - when it exceeds this value, we set it back to GC_RESPONSE_READY
00100 const int EthernetHandler::maxResponseReadyThreadSignalCode = GC_RESPONSE_READY + 1000;
00101 
00102 
00103 //#define CLIENT_SET_BLOCKING_FALSE
00104 
00105                               
00106 bool EthernetHandler::GCCommandReceived(void)
00107 { 
00108 #ifdef DO_USB_IN_THIS_THREAD
00109     return false; // We will deal with this ourselves
00110 #else
00111     return receivedGCCommand;
00112 #endif
00113 }
00114 
00115 
00116 /*
00117     Caller tells us the ID of the thread we are to signal 
00118     when we have received a command (over the Ethernet link)
00119     to be sent to the GC.
00120 */
00121 void EthernetHandler::SetMainThreadId(osThreadId threadId)
00122 {
00123     mainThreadId = threadId;
00124     
00125     mainThreadIdSet = true;
00126 }
00127 
00128 /*
00129     Caller gives us a pointer to the Ethernet server 
00130     we are to use.
00131 */
00132 void EthernetHandler::SetEthernetServer(TCPSocketServer* server)
00133 {
00134     ethernetServer = server;
00135 }
00136 
00137 /*
00138     Caller gives us pointers for the USB link to the GC, 
00139     to allow us to communicate with it ourselves
00140 */
00141 void EthernetHandler::SetUsbGC(USBDeviceConnected* newUsbDevice, USBHostGC* newUsbHostGC)
00142 {
00143     usbDevice = newUsbDevice;
00144     usbHostGC = newUsbHostGC;
00145 }
00146     
00147 
00148 /*
00149     Passes back to the caller, the command we have received over the Ethernet link
00150     to be sent to the GC (assume the caller is going to communicate with the GC).
00151     
00152     Args: pointer to a buffer into which we will copy the command
00153           pointer to an integer into which we will place the value the caller
00154           must use to tell us that it has given us the response
00155 */
00156 void EthernetHandler::GetGCCommand(char *commandBuffer, int* responseThreadSignalCodeToUse)
00157 {
00158     strcpy(commandBuffer, gcCommand);
00159     
00160     SetupNextResponseReadyThreadSignalCode();
00161     
00162     *responseThreadSignalCodeToUse = responseReadyThreadSignalCode;
00163 }
00164    
00165 /*
00166     Set 'responseReadyThreadSignalCode' to the next legal value
00167 */ 
00168 void EthernetHandler::SetupNextResponseReadyThreadSignalCode(void)
00169 {
00170     ++responseReadyThreadSignalCode;
00171     
00172     if(responseReadyThreadSignalCode > maxResponseReadyThreadSignalCode) {
00173         responseReadyThreadSignalCode = GC_RESPONSE_READY;
00174     }
00175 }
00176 
00177     
00178 /*
00179     Function to allow the caller to pass to us,
00180     the response it got back from the GC.
00181     We copy it into our buffer, ready to be sent back 
00182     to the Ethernet client.
00183     
00184     Caller must set this *before* signaling GC_RESPONSE_READY
00185     *********************************************************
00186     
00187     Args: pointer to a buffer containing the GC response
00188 */
00189 void EthernetHandler::SetGCResponse(char *response)
00190 {
00191     strcpy(gcResponse, response);
00192 }
00193 
00194 
00195 /*
00196     Sends a command (known as a 'report') to the GC, and returns the response.
00197     
00198     Args: pointer to (null-terminated) command to use
00199           pointer to buffer to contain the (also null-terminated) response
00200           
00201     No return code.
00202 */
00203 void EthernetHandler::SetGCDeviceReport(char *cmd, char *response)
00204 {
00205     if((usbHostGC != NULL) && (usbDevice != NULL)) {
00206         
00207         //strcpy(response, "BEFORE"); // Debug
00208 
00209         // Guard against simultaneous calls to usbHostGC->SetDeviceReport - 
00210         // it is not re-entrant (and nor is the GC)
00211         while(usbHostGC->ExecutingSetDeviceReport()) {
00212             Thread::wait(1); // Give other threads a chance
00213         }
00214 
00215         //strcpy(response, "AFTER 1"); // Debug
00216         
00217         usbHostGC->SetDeviceReport(usbDevice, cmd, response);
00218 
00219         //strcpy(response, "AFTER 2"); // Debug
00220     }
00221 }
00222     
00223 
00224 /*
00225     Handles the next Ethernet message from the specified client.
00226     Returns true if there was a message, false if not.
00227 */
00228 bool EthernetHandler::HandleEthernetMessage(TCPSocketConnection* client)
00229 {
00230     if(mainThreadIdSet) {
00231 
00232         receivedGCCommand = false;
00233         
00234         int n = client->receive(gcCommand, sizeof(gcCommand));
00235         if (n <= 0) return false;
00236         
00237         gcCommand[n] = '\0';
00238         
00239         timer.reset();
00240         timer.start();
00241     
00242 //#define ALLOW_DEBUG_PRINTS_HERE
00243 #ifdef ALLOW_DEBUG_PRINTS_HERE
00244     char buff[300];
00245     sprintf(buff, "ETH - command received: \"%s\"", gcCommand);
00246     SpecialDebugPrint(buff, 50, 350);
00247 #endif // ALLOW_DEBUG_PRINTS_HERE
00248 
00249         receivedGCCommand = true;
00250         
00251 #ifdef USE_LED_FOR_DEBUGGING
00252         // Turn LED 1 on while we signal the main thread that we have a command, and while we wait for the main thread to respond
00253         // - or, if we are doing USB in this thread, while *we* send the command to the GC and wait for the GC to respond
00254         SetLed1(true);
00255 #endif // USE_LED_FOR_DEBUGGING
00256 
00257 
00258 #ifdef DO_USB_IN_THIS_THREAD
00259 
00260         bool commandIdentified = false;
00261 
00262         if(IsStartOfMethodCommand(gcCommand)) {
00263             commandIdentified = true;
00264             osSignalSet(mainThreadId, STARTED_DOWNLOADING_METHOD);
00265         }
00266 
00267         SetGCDeviceReport(gcCommand, gcResponse);
00268 
00269         if(!commandIdentified) {
00270             if(IsEndOfMethodCommand(gcCommand)) {
00271                 commandIdentified = true;
00272                 osSignalSet(mainThreadId, FINISHED_DOWNLOADING_METHOD);
00273             }
00274         }
00275         
00276         if(!commandIdentified) {       
00277             // Tell main thread if CRUN, CHON or CHOF were sent to the GC, and were successful - 
00278             // i.e. acknowledged with DACK - so that it can update the display.
00279             // Also now CSTP and CABT
00280             if((gcResponse[0] == 'D') && (gcResponse[1] == 'A') && (gcResponse[2] == 'C') && (gcResponse[3] == 'K')) { /// i.e. "DACK"
00281                 if(gcCommand[0] == 'C') { // Begins with 'C' - must be a command of some kind
00282                     if((gcCommand[1] == 'R') && (gcCommand[2] == 'U') && (gcCommand[3] == 'N')) { // i.e. "CRUN"
00283                         commandIdentified = true;
00284                         osSignalSet(mainThreadId, CRUN_COMMAND_SENT);
00285                     }
00286     
00287                     if(!commandIdentified) {       
00288                         if((gcCommand[1] == 'H') && (gcCommand[2] == 'O')) {
00289                             if(gcCommand[3] == 'N') { // i.e. "CHON"
00290                                 commandIdentified = true;
00291                                 osSignalSet(mainThreadId, CHON_COMMAND_SENT);
00292                             }
00293             
00294                             if(!commandIdentified) {       
00295                                 if(gcCommand[3] == 'F') {  // i.e. "CHOF"
00296                                     commandIdentified = true;
00297                                     osSignalSet(mainThreadId, CHOF_COMMAND_SENT);
00298                                 }
00299                             }
00300                         }
00301                     }
00302                     
00303                     if(!commandIdentified) {       
00304                         if(((gcCommand[1] == 'S') && (gcCommand[2] == 'T') && (gcCommand[3] == 'P'))      // i.e. "CSTP"
00305                         || ((gcCommand[1] == 'A') && (gcCommand[2] == 'B') && (gcCommand[3] == 'T'))) {   // i.e. "CABT"
00306                             commandIdentified = true;
00307                             osSignalSet(mainThreadId, ABORT_RUN_COMMAND_SENT);
00308                         }
00309                     }                    
00310                 }
00311             }
00312         }        
00313 #else
00314         // Signal the main thread that we have a command to send to the GC
00315         osSignalSet(mainThreadId, GC_COMMAND_READY);
00316         
00317         // Now wait for it to tell us it has 'got' the command - try and keep synchronised
00318         // (i.e. so that we do not get other commands before the main thread has dealt with this one,
00319         // and so that the commands and responses do not get out of step)
00320         Thread::signal_wait(GOT_GC_COMMAND);
00321         
00322         
00323         // Main thread must give us the response *now* - *before* signaling 'response ready' - see above
00324         // *********************************************************************************************
00325         
00326         // We tell the main thread to use a different response code for each command, 
00327         // to try and ensure that the commands and responses do not get out of step
00328         
00329         // Wait forever - do not timeout (no point in continuing without a response)
00330         Thread::signal_wait(responseReadyThreadSignalCode);
00331         
00332         // Main thread has put the response in 'gcResponse'
00333         
00334         receivedGCCommand = false; // Ready for the next command
00335     
00336 #endif // DO_USB_IN_THIS_THREAD
00337         
00338                    
00339 #ifdef USE_LED_FOR_DEBUGGING
00340         // We have received the command response - turn LED 1 off
00341         SetLed1(false);
00342 #endif // USE_LED_FOR_DEBUGGING
00343 
00344         // Pass the response back to the client
00345         client->send_all(gcResponse, strlen(gcResponse));
00346         
00347         timer.stop();
00348         
00349 #ifdef ALLOW_DEBUG_PRINTS_HERE
00350     sprintf(buff, "ETH - response sent: \"%s\"", gcResponse);
00351     SpecialDebugPrint(buff, 50, 380);
00352 
00353     if((gcResponse[1] == gcCommand[1]) && (gcResponse[2] == gcCommand[2]) && (gcResponse[3] == gcCommand[3])) {
00354         SpecialDebugPrint("ETH - command and response match", 50, 400);
00355     } else {
00356         SpecialDebugPrint("ETH - command and response *** do not match ***", 50, 400);
00357     }
00358     
00359     int read_ms = timer.read_ms();
00360     if(read_ms > 1000) { // We are interested only in suspiciously long response times (that may make Ellution time out)
00361         sprintf(buff, "ETH - time taken for %s: %d ms", gcCommand, read_ms);
00362         SpecialDebugPrint(buff, 50, 420);    
00363     }
00364 #endif // ALLOW_DEBUG_PRINTS_HERE
00365 
00366         //receivedGCCommand = false; // Ready for the next command
00367     
00368         return true;
00369     }
00370     
00371     // 'else'
00372     return false;
00373 }
00374 
00375 
00376 /*
00377     This is the infinite loop that handles Ethernet traffic on its own thread.
00378     Once started, it never exits.
00379 */
00380 void EthernetHandler::HandlerFunction(void const *argument)
00381 {
00382     if(mainThreadIdSet && (ethernetServer != NULL)) {
00383 
00384         // We will now sit in this loop until the end of time, or this thread gets terminated, or the system powers off, whichever happens soonest...
00385         while(true) {
00386     
00387             TCPSocketConnection client;
00388     
00389             // Look for a client, but do not hang if there isn't one
00390             ethernetServer->set_blocking(false); // Timeout after (1.5)s
00391             bool clientFound = (ethernetServer->accept(client) == 0);
00392             if(clientFound) {
00393 #ifdef CLIENT_SET_BLOCKING_FALSE
00394                 client.set_blocking(false); // Default timeout (1.5 sec)
00395                 //client.set_blocking(false, 60000); // Timeout after 1 minute (comms somehow works better than with 1.5sec)
00396 #else
00397                 // No - block this thread until we get a command - what else would we do?
00398                 client.set_blocking(true); 
00399                 // But note that, on the face of it, set_blocking(true) means that we will never close the first client connection we open - 
00400                 // if the client goes away, we will just sit waiting forever (in HandleEthernetMessage) for the next command(?). 
00401                 // In this scenario, no subsequent client will be able to establish a connection.
00402 #endif // CLIENT_SET_BLOCKING_FALSE
00403     
00404                 while (true) 
00405                 {
00406                     if(!HandleEthernetMessage(&client)) break;
00407                 }
00408                                         
00409                 client.close();
00410             } 
00411         }
00412     }
00413 }
00414 
00415     
00416 
00417 
00418