plotly interface based on ardunio sample code

Dependents:   Plotly_HelloWorld

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers plotly.cpp Source File

plotly.cpp

00001 #include "plotly.h"
00002 #include "mbed.h"
00003 
00004 #define plotlyURL "plot.ly"
00005 #define dataURL "arduino.plot.ly"
00006 
00007 plotly::plotly (const char *username, const char *api_key, const char* stream_tokens[], const char *filename, int nTraces)
00008 {
00009     log_level = 3;  // 0 = Debugging, 1 = Informational, 2 = Status, 3 = Errors, 4 = Quiet (// Serial Off)
00010     dry_run = false;
00011     username_ = username;
00012     api_key_ = api_key;
00013     stream_tokens_ = stream_tokens;
00014     filename_ = filename;
00015     maxpoints = 30;
00016     world_readable = true;
00017     convertTimestamp = false;
00018     timezone = "America/Montreal";
00019     fileopt = "overwrite";
00020 
00021     nTraces_ = nTraces;
00022 
00023     sockets = (TCPSocketConnection **)malloc(sizeof(TCPSocketConnection *) * nTraces_);
00024     for (int i = 0; i< nTraces_; i++) {
00025         *(sockets+i) = NULL;
00026     }
00027     initalised = false;
00028 
00029 }
00030 
00031 
00032 plotly::~plotly()
00033 {
00034     closeStreams();
00035     if (sockets)
00036         free(sockets);
00037 }
00038 
00039 
00040 bool plotly::init()
00041 {
00042     //
00043     //  Create plot with a REST post to plotly
00044     //  See the clientresp section of https://plot.ly/rest/ for details
00045     //
00046     if(dry_run && log_level < 3) {
00047         fprintf(stderr,"... This is a dry run, we are not connecting to plotly's servers...\n");
00048     } else if(log_level < 3) {
00049         fprintf(stderr,"... Attempting to connect to plotly's REST servers\n");
00050     }
00051 
00052     if (!dry_run) {
00053         int pause = 1;
00054         *sockets = new TCPSocketConnection();
00055         if (!*sockets)
00056             return false;
00057         while ((*sockets)->connect(plotlyURL, 80) < 0) {
00058             if (pause > 30) {
00059                 fprintf(stderr,"Failed to connect. :-(\n");
00060                 delete (*sockets);
00061                 return false;
00062             }
00063             fprintf(stderr,"... Couldn\'t connect to plotly's REST servers... trying again!\n");
00064             wait(pause);
00065             pause *= 2;
00066         }
00067     }
00068 
00069     if(log_level < 3) fprintf(stderr,"... Connected to plotly's REST servers\n");
00070 
00071     if(log_level < 3) fprintf(stderr,"... Sending HTTP Post to plotly\n");
00072 
00073     print_("POST /clientresp HTTP/1.1\r\n");
00074     print_("Host: 107.21.214.199\r\n");
00075     print_("User-Agent: Arduino/0.5.1\r\n");
00076     print_("plotly-streamtoken: ");
00077     for (int i = 0; i<nTraces_; i++) {
00078         print_(stream_tokens_[i]);
00079         if ( i < (nTraces_-1) )
00080             print_(",");
00081     }
00082     printNetTerminator_();
00083 
00084     print_("Content-Length: ");
00085 
00086     unsigned long lineLen = snprintf(buffer,k_bufferSize,"version=2.3&origin=plot&platform=arduino&un=%s&key=%s&args=[",username_,api_key_);
00087     for(int i=0; i<nTraces_; i++) {
00088         lineLen += snprintf((buffer+lineLen),k_bufferSize-lineLen,"{\"y\": [], \"x\": [], \"type\": \"scatter\", \"stream\": {\"token\": \"%s\", \"maxpoints\": %d}}%s",stream_tokens_[i],maxpoints,((nTraces_ > 1) && (i != nTraces_-1))?", ":"");
00089     }
00090     lineLen += snprintf((buffer+lineLen),k_bufferSize-lineLen,"]&kwargs={\"fileopt\": \"%s\", \"filename\": \"%s\", \"world_readable\": %s}",fileopt,filename_,world_readable?"true":"false");
00091 
00092     print_(lineLen);
00093     printNetTerminator_();
00094     printNetTerminator_();
00095 
00096     sendFormatedText(buffer,lineLen);
00097 
00098     printNetTerminator_();
00099 
00100     //
00101     // Wait for a response
00102     // Parse the response for the "All Streams Go!" and proceed to streaming
00103     // if we find it
00104     //
00105 
00106     char allStreamsGo[] = "All Streams Go!";
00107     int asgCnt = 0; // asg stands for All Streams Go
00108     char url[] = "\"url\": \"http://107.21.214.199/~";
00109     char fid[4];
00110     int fidCnt = 0;
00111     int urlCnt = 0;
00112     int usernameCnt = 0;
00113     bool proceed = false;
00114     bool fidMatched = false;
00115     char c;
00116 
00117     if(log_level < 2) {
00118         fprintf(stderr,"... Sent message, waiting for plotly's response...\n");
00119     }
00120 
00121     if(!dry_run) {
00122         while(!proceed) {
00123             int32_t dataIn = (*sockets)->receive(buffer,k_bufferSize -1);
00124             if (dataIn < 0) {
00125                 if(log_level < 3) fprintf(stderr,"error reading network socket\n");
00126                 break;
00127             }
00128             if(dataIn > 0) {
00129                 buffer[dataIn]=0;
00130 
00131                 if(log_level < 2) fprintf(stderr,buffer);
00132 
00133                 for (int i = 0; i<dataIn; i++) {
00134                     c = buffer[i];
00135                     //
00136                     // Attempt to read the "All streams go" msg if it exists
00137                     // by comparing characters as they roll in
00138                     //
00139 
00140                     if(asgCnt == strlen(allStreamsGo) && !proceed) {
00141                         proceed = true;
00142                     } else if(allStreamsGo[asgCnt]==c) {
00143                         asgCnt += 1;
00144                     } else if(asgCnt > 0) {
00145                         // reset counter
00146                         asgCnt = 0;
00147                     }
00148 
00149                     //
00150                     // Extract the last bit of the URL from the response
00151                     // The url is in the form http://107.21.214.199/~USERNAME/FID
00152                     // We'll character-count up through char url[] and through username_, then start
00153                     // filling in characters into fid
00154                     //
00155 
00156                     if(log_level < 3) {
00157                         if(url[urlCnt]==c && urlCnt < strlen(url)) {
00158                             urlCnt += 1;
00159                         } else if(urlCnt > 0 && urlCnt < strlen(url)) {
00160                             // Reset counter
00161                             urlCnt = 0;
00162                         }
00163                         if(urlCnt == strlen(url) && fidCnt < 4 && !fidMatched) {
00164                             // We've counted through the url, start counting through the username
00165                             if(usernameCnt < strlen(username_)+2) {
00166                                 usernameCnt += 1;
00167                             } else {
00168                                 // the url ends with "
00169                                 if(c != '"') {
00170                                     fid[fidCnt] = c;
00171                                     fidCnt += 1;
00172                                 } else if(fidCnt>0) {
00173                                     fidMatched = true;
00174                                 }
00175 
00176                             }
00177                         }
00178                     }
00179                 }
00180             }
00181             wait(0.1);
00182         }
00183     }
00184 
00185     if(!dry_run && !proceed && log_level < 4) {
00186         fprintf(stderr,"... Error initializing stream, aborting.\n");
00187     }
00188 
00189     if(!dry_run && proceed && log_level < 3) {
00190         fprintf(stderr,"... A-ok from plotly, All Streams Go!\n");
00191         if(fidMatched) {
00192             fprintf(stderr,"... View your streaming plot here: https://plot.ly/~");
00193             fprintf(stderr,username_);
00194             fprintf(stderr,"/");
00195             for(int i=0; i<fidCnt; i++) {
00196                 fprintf(stderr,"%c",fid[i]);
00197             }
00198             fprintf(stderr,"\n");
00199         }
00200     }
00201 
00202     if (proceed || dry_run) {
00203         initalised = true;
00204     }
00205     if (*sockets) {
00206         delete *sockets;
00207         *sockets=NULL;
00208     }
00209     return initalised;
00210 }
00211 
00212 void plotly::openStreams()
00213 {
00214     for (int i = 0; i< nTraces_; i++) {
00215         openStream(i);
00216     }
00217 }
00218 
00219 void plotly::closeStreams()
00220 {
00221     for (int i = 0; i< nTraces_; i++) {
00222         closeStream(i);
00223     }
00224 }
00225 
00226 
00227 bool plotly::openStream(int stream)
00228 {
00229 
00230     if (stream >= nTraces_)
00231         return false;
00232 
00233     if (!initalised)
00234         return false;
00235 
00236     //
00237     // Start request to stream servers
00238     //
00239 
00240     if (*(sockets+stream) != NULL) {
00241         delete *(sockets+stream);
00242         *(sockets+stream) = NULL;
00243     }
00244 
00245     if(log_level < 3) fprintf(stderr,"... Connecting to plotly's streaming servers...\n");
00246 
00247     if (!dry_run) {
00248         int pause = 1;
00249         *(sockets + stream) = new TCPSocketConnection();
00250         while ((*(sockets + stream))->connect(dataURL, 80) < 0) {
00251             if (pause > 30) {
00252                 fprintf(stderr,"Failed to connect. :-(\n");
00253                 delete *(sockets + stream);
00254                 return false;
00255             }
00256             fprintf(stderr,"... Couldn\'t connect to plotly's REST servers... trying again!\n");
00257             wait(pause);
00258             pause *= 2;
00259         }
00260     }
00261 
00262 
00263     if(log_level < 3) fprintf(stderr,"... Connected to plotly's streaming servers\n... Initializing stream %d\n",stream);
00264 
00265     print_("POST / HTTP/1.1\r\n",stream);
00266     print_("Host: arduino.plot.ly\r\n",stream);
00267     print_("User-Agent: Python\r\n",stream);
00268     print_("Transfer-Encoding: chunked\r\n",stream);
00269     print_("Connection: close\r\n",stream);
00270     print_("plotly-streamtoken: ",stream);
00271     print_(stream_tokens_[stream],stream);
00272     printNetTerminator_(stream);
00273     if(convertTimestamp) {
00274         print_("plotly-convertTimestamp: \"",stream);
00275         print_(timezone,stream);
00276         print_("\"",stream);
00277         printNetTerminator_(stream);
00278     }
00279     printNetTerminator_(stream);
00280 
00281     if(log_level < 3) fprintf(stderr,"... Done initializing, ready to stream!\n");
00282     return true;
00283 }
00284 
00285 void plotly::closeStream( int stream)
00286 {
00287     if (stream >= nTraces_)
00288         return;
00289 
00290     if (*(sockets+stream) != NULL) {
00291         if ((*(sockets+stream))->is_connected()) {
00292             print_("0\r\n\r\n",stream);
00293             (*(sockets+stream))->close();
00294         }
00295         delete *(sockets+stream);
00296         *(sockets+stream)=NULL;
00297     }
00298 }
00299 
00300 void plotly::reconnectStream(int number)
00301 {
00302     while(!dry_run && (!(*(sockets+number)) || !(*(sockets+number))->is_connected())) {
00303         if(log_level<4) fprintf(stderr,"... Disconnected from streaming servers\n");
00304         closeStream(number);
00305         openStream(number);
00306     }
00307 }
00308 
00309 
00310 void plotly::plot(unsigned long x, int y, int stream)
00311 {
00312     if (!initalised)
00313         return;
00314 
00315     reconnectStream(stream);
00316 
00317     // need to prefix with the length so print it once to get the content lenght and then a second time with the prefix.
00318     int len = snprintf(buffer,k_bufferSize,"{\"x\": %lu, \"y\": %d}\n", x,y);
00319     len = snprintf(buffer,k_bufferSize,"%x\r\n{\"x\": %lu, \"y\": %d}\n\r\n",len, x,y);
00320     sendFormatedText(buffer,len,stream);
00321 }
00322 
00323 void plotly::plot(unsigned long x, float y, int stream)
00324 {
00325     if (!initalised)
00326         return;
00327 
00328     reconnectStream(stream);
00329 
00330     // need to prefix with the length so print it once to get the content lenght and then a second time with the prefix.
00331     int len = snprintf(buffer,k_bufferSize,"{\"x\": %lu, \"y\": %.3f}\n", x,y);
00332     len = snprintf(buffer,k_bufferSize,"%x\r\n{\"x\": %lu, \"y\": %.3f}\n\r\n",len, x,y);
00333     sendFormatedText(buffer,len,stream);
00334 }
00335 
00336 void plotly::plot(float x, float y, int stream)
00337 {
00338     if (!initalised)
00339         return;
00340 
00341     reconnectStream(stream);
00342     // need to prefix with the length so print it once to get the content lenght and then a second time with the prefix.
00343     int len = snprintf(buffer,k_bufferSize,"{\"x\": %.3f, \"y\": %.3f}\n", x,y);
00344     len = snprintf(buffer,k_bufferSize,"%x\r\n{\"x\": %.3f, \"y\": %.3f}\n\r\n",len, x,y);
00345     sendFormatedText(buffer,len,stream);
00346 }
00347 
00348 
00349 bool plotly::print_(int d, int stream)
00350 {
00351     char smallBuffer[10];
00352     int32_t len = snprintf(smallBuffer,10,"%d",d);
00353     return sendFormatedText(smallBuffer,len,stream);
00354 }
00355 
00356 bool plotly::print_(unsigned long d, int stream)
00357 {
00358     char smallBuffer[12];
00359     int32_t len = snprintf(smallBuffer,12,"%lu",d);
00360     return sendFormatedText(smallBuffer,len,stream);
00361 }
00362 
00363 bool plotly::print_(float d, int stream)
00364 {
00365     char smallBuffer[12];
00366     int32_t len = snprintf(smallBuffer,12,"%f",d);
00367     return sendFormatedText(smallBuffer,len,stream);
00368 }
00369 
00370 bool plotly::printNetTerminator_(int stream)
00371 {
00372     return sendFormatedText("\r\n",2,stream);
00373 }
00374 
00375 
00376 
00377 bool plotly::print_(const char *d, int stream)// strings could be long, use the big buffer
00378 {
00379     int32_t len = snprintf(buffer,k_bufferSize,"%s",d);
00380     return sendFormatedText(buffer,len,stream);
00381 }
00382 
00383 bool plotly::printHex_(uint16_t d, int stream)
00384 {
00385     int32_t len = snprintf(buffer,k_bufferSize,"%X",d);
00386     return sendFormatedText(buffer,len,stream);
00387 }
00388 
00389 bool plotly::sendFormatedText(char* data, int size, int stream)
00390 {
00391     if(log_level < 2) {
00392         fprintf(stderr,"%s",data);
00393     }
00394     if(!dry_run) {
00395         if (!*(sockets+stream)) {
00396             fprintf(stderr,"\nTX failed, No network socket exists\n");
00397             return false;
00398         }
00399         if (!((*(sockets+stream))->is_connected())) {
00400             fprintf(stderr,"\nTX failed, Network socket not connected\n");
00401             return false;
00402         }
00403 
00404         int32_t sent = (*(sockets+stream))->send_all(data,size);
00405         if (sent == size)
00406             return true;
00407         else {
00408             fprintf(stderr,"\nTX failed to send _%s_ Sent %d of %d bytes\n",data,sent,size);
00409             return false;
00410         }
00411     } else
00412         return true;
00413 }
00414