plotly interface based on ardunio sample code

Dependents:   Plotly_HelloWorld

Library for plotting a simple x/y scatter chart on the plot.ly website.

See plotly_HelloWorld for sample usage.

Revision:
6:e57d6e9313f4
Parent:
3:967be3d46701
Child:
7:9409a72ab6c0
--- a/plotly.cpp	Fri Jul 11 08:08:06 2014 +0000
+++ b/plotly.cpp	Tue Jul 29 11:45:09 2014 +0000
@@ -4,7 +4,7 @@
 #define plotlyURL "plot.ly"
 #define dataURL "arduino.plot.ly"
 
-plotly::plotly(char *username, char *api_key, char* stream_tokens[], char *filename, int nTraces)
+plotly::plotly(const char *username, const char *api_key, const char* stream_tokens[], const char *filename, int nTraces)
 {
     log_level = 0;  // 0 = Debugging, 1 = Informational, 2 = Status, 3 = Errors, 4 = Quiet (// Serial Off)
     dry_run = false;
@@ -16,18 +16,23 @@
     maxpoints = 30;
     fibonacci_ = 1;
     world_readable = true;
-    convertTimestamp = true;
+    convertTimestamp = false;
     timezone = "America/Montreal";
     fileopt = "overwrite";
 
-    socket = NULL;
+    sockets = (TCPSocketConnection **)malloc(sizeof(TCPSocketConnection *) * nTraces);
+    for (int i = 0; i< nTraces; i++) {
+        *(sockets+i) = NULL;
+    }
     initalised = false;
 }
 
 
 plotly::~plotly()
 {
-    closeStream();
+    closeStreams();
+    if (sockets)
+        free(sockets);
 }
 
 bool plotly::init()
@@ -45,8 +50,10 @@
 //    socket.set_blocking(false);
 
     if (!dry_run) {
-        socket = new TCPSocketConnection();
-        while (socket->connect(plotlyURL, 80) < 0) {
+        *sockets = new TCPSocketConnection();
+        if (!*sockets)
+            return false;
+        while ((*sockets)->connect(plotlyURL, 80) < 0) {
             fprintf(stderr,"... Couldn\'t connect to plotly's REST servers... trying again!\n");
             wait(1);
         }
@@ -60,94 +67,35 @@
     print_("Host: 107.21.214.199\r\n");
     print_("User-Agent: Arduino/0.5.1\r\n");
     print_("plotly-streamtoken: ");
-    print_(stream_tokens_[0]);
+    for (int i = 0; i<nTraces_; i++) {
+        print_(stream_tokens_[i]);
+        if ( i < (nTraces_-1) )
+            print_(",");
+    }
     print_("\r\n");
 
     print_("Content-Length: ");
-    int contentLength = 126 + len_(username_) + len_(fileopt) + nTraces_*(87+len_(maxpoints)) + (nTraces_-1)*2 + len_(filename_);
-    if(world_readable) {
-        contentLength += 4;
-    } else {
-        contentLength += 5;
-    }
-    // contentLength =
-    //   44  // first part of querystring below
-    // + len_(username)  // upper bound on username length
-    // + 5   // &key=
-    // + 10  // api_key length
-    // + 7  // &args=[...
-    // + nTraces*(87+len(maxpoints)) // len({\"y\": [], \"x\": [], \"type\": \"scatter\", \"stream\": {\"token\": \") + 10 + len(\", "maxpoints": )+len(maxpoints)+len(}})
-    // + (nTraces-1)*2 // ", " in between trace objects
-    // + 22  // ]&kwargs={\"fileopt\": \"
-    // + len_(fileopt)
-    // + 16  // \", \"filename\": \"
-    // + len_(filename)
-    // + 21 // ", "world_readable":
-    // + 4 if world_readable, 5 otherwise
-    // + 1   // closing }
-    //------
-    // 126 + len_(username) + len_(fileopt) + nTraces*(86+len(maxpoints)) + (nTraces-1)*2 + len_(filename)
-    //
-    // Terminate headers with new lines
-
 
 // big buffer method to generate the string so that length can be measured directly.
 
-
-//    fprintf(stderr,"AutoVersion:\n");
     int lineLen = snprintf(buffer,k_bufferSize,"version=2.3&origin=plot&platform=arduino&un=%s&key=%s&args=[",username_,api_key_);
     for(int i=0; i<nTraces_; i++) {
         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))?", ":"");
     }
     lineLen += snprintf((buffer+lineLen),k_bufferSize-lineLen,"]&kwargs={\"fileopt\": \"%s\", \"filename\": \"%s\", \"world_readable\": %s}",fileopt,filename_,world_readable?"true":"false");
 
-//    fprintf(stderr,buffer);
-//    fprintf(stderr,"\nLen = %d",lineLen);
-
     print_(lineLen);
     print_("\r\n\r\n");
-    
+
     lineLen = snprintf(buffer,k_bufferSize,"version=2.3&origin=plot&platform=arduino&un=%s&key=%s&args=[",username_,api_key_);
     for(int i=0; i<nTraces_; i++) {
         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))?", ":"");
     }
     lineLen += snprintf((buffer+lineLen),k_bufferSize-lineLen,"]&kwargs={\"fileopt\": \"%s\", \"filename\": \"%s\", \"world_readable\": %s}",fileopt,filename_,world_readable?"true":"false");
     sendFormatedText(buffer,lineLen);
-    
+
     print_("\r\n");
-/*            
-    // Start printing querystring body
-    print_("version=2.2&origin=plot&platform=arduino&un=");
-    print_(username_);
-    print_("&key=");
-    print_(api_key_);
-    print_("&args=[");
-    // print a trace for each token supplied
-    for(int i=0; i<nTraces_; i++) {
-        print_("{\"y\": [], \"x\": [], \"type\": \"scatter\", \"stream\": {\"token\": \"");
-        print_(stream_tokens_[i]);
-        print_("\", \"maxpoints\": ");
-        print_(maxpoints);
-        print_("}}");
-        if(nTraces_ > 1 && i != nTraces_-1) {
-            print_(", ");
-        }
-    }
-    print_("]&kwargs={\"fileopt\": \"");
-    print_(fileopt);
-    print_("\", \"filename\": \"");
-    print_(filename_);
-    print_("\", \"world_readable\": ");
-    if(world_readable) {
-        print_("true");
-    } else {
-        print_("false");
-    }
-    print_("}");
-    // final newline to terminate the POST
-    print_("\r\n");
-*/
-
+ 
     //
     // Wait for a response
     // Parse the response for the "All Streams Go!" and proceed to streaming
@@ -171,7 +119,7 @@
 
     if(!dry_run) {
         while(!proceed) {
-            int32_t dataIn = socket->receive(buffer,k_bufferSize -1);
+            int32_t dataIn = (*sockets)->receive(buffer,k_bufferSize -1);
             if (dataIn < 0) {
                 if(log_level < 3) fprintf(stderr,"error reading network socket\n");
                 break;
@@ -188,7 +136,7 @@
                     // by comparing characters as they roll in
                     //
 
-                    if(asgCnt == len_(allStreamsGo) && !proceed) {
+                    if(asgCnt == strlen(allStreamsGo) && !proceed) {
                         proceed = true;
                     } else if(allStreamsGo[asgCnt]==c) {
                         asgCnt += 1;
@@ -205,15 +153,15 @@
                     //
 
                     if(log_level < 3) {
-                        if(url[urlCnt]==c && urlCnt < len_(url)) {
+                        if(url[urlCnt]==c && urlCnt < strlen(url)) {
                             urlCnt += 1;
-                        } else if(urlCnt > 0 && urlCnt < len_(url)) {
+                        } else if(urlCnt > 0 && urlCnt < strlen(url)) {
                             // Reset counter
                             urlCnt = 0;
                         }
-                        if(urlCnt == len_(url) && fidCnt < 4 && !fidMatched) {
+                        if(urlCnt == strlen(url) && fidCnt < 4 && !fidMatched) {
                             // We've counted through the url, start counting through the username
-                            if(usernameCnt < len_(username_)+2) {
+                            if(usernameCnt < strlen(username_)+2) {
                                 usernameCnt += 1;
                             } else {
                                 // the url ends with "
@@ -244,24 +192,42 @@
             fprintf(stderr,username_);
             fprintf(stderr,"/");
             for(int i=0; i<fidCnt; i++) {
-                fprintf(stderr,"%d ",fid[i]);
+                fprintf(stderr,"%c ",fid[i]);
             }
             fprintf(stderr,"\n");
         }
     }
 
     if (proceed || dry_run) {
-      initalised = true;
+        initalised = true;
     }
-    if (socket) {
-      delete socket;
-      socket=NULL;
-      }
+    if (*sockets) {
+        delete *sockets;
+        *sockets=NULL;
+    }
     return initalised;
 }
 
-void plotly::openStream()
+void plotly::openStreams()
+{
+    for (int i = 0; i< nTraces_; i++) {
+        openStream(i);
+    }
+}
+
+void plotly::closeStreams()
 {
+    for (int i = 0; i< nTraces_; i++) {
+        closeStream(i);
+    }
+}
+
+
+void plotly::openStream(int stream)
+{
+
+    if (stream >= nTraces_)
+        return;
 
     if (!initalised)
         return;
@@ -269,225 +235,157 @@
     // Start request to stream servers
     //
 
-    if (socket) {
-        delete socket;
-        socket = NULL;
-        }
-        
+    if (*(sockets+stream) != NULL) {
+        delete *(sockets+stream);
+        *(sockets+stream) = NULL;
+    }
 
     if(log_level < 3) fprintf(stderr,"... Connecting to plotly's streaming servers...\n");
 
-
-    if (!dry_run && !socket) {
-        socket = new TCPSocketConnection();
-        while (socket->connect(dataURL, 80) < 0) {
+    if (!dry_run) {
+        *(sockets + stream) = new TCPSocketConnection();
+        while ((*(sockets + stream))->connect(dataURL, 80) < 0) {
             fprintf(stderr,"... Couldn\'t connect to servers... trying again!\n");
             wait(10);
         }
     }
 
-    if(log_level < 3) fprintf(stderr,"... Connected to plotly's streaming servers\n... Initializing stream\n");
+    if(log_level < 3) fprintf(stderr,"... Connected to plotly's streaming servers\n... Initializing stream %d\n",stream);
 
-    print_("POST / HTTP/1.1\r\n");
-    print_("Host: arduino.plot.ly\r\n");
-    print_("User-Agent: Python\r\n");
-    print_("Transfer-Encoding: chunked\r\n");
-    print_("Connection: close\r\n");
-    print_("plotly-streamtoken: ");
-    print_(stream_tokens_[0]);
-    print_("\r\n");
-//    if(convertTimestamp) {
-//        print_("plotly-convertTimestamp: \"");
-//        print_(timezone);
-//        print_("\"");
-//        print_("\r\n");
-//    }
-    print_("\r\n");
+    print_("POST / HTTP/1.1\r\n",stream);
+    print_("Host: arduino.plot.ly\r\n",stream);
+    print_("User-Agent: Python\r\n",stream);
+    print_("Transfer-Encoding: chunked\r\n",stream);
+    print_("Connection: close\r\n",stream);
+    print_("plotly-streamtoken: ",stream);
+    print_(stream_tokens_[stream],stream);
+    print_("\r\n",stream);
+    if(convertTimestamp) {
+        print_("plotly-convertTimestamp: \"");
+        print_(timezone);
+        print_("\"");
+        print_("\r\n");
+    }
+    print_("\r\n",stream);
 
     if(log_level < 3) fprintf(stderr,"... Done initializing, ready to stream!\n");
 }
 
-void plotly::closeStream()
+void plotly::closeStream( int stream)
 {
-    if (socket) {
-        if (socket->is_connected()) {
-          print_("0\r\n\r\n");
-          socket->close();
+    if (stream >= nTraces_)
+        return;
+
+    if (*(sockets+stream) != NULL) {
+        if ((*(sockets+stream))->is_connected()) {
+            print_("0\r\n\r\n",stream);
+            (*(sockets+stream))->close();
         }
-        delete socket;
-    socket=NULL;
-    }
-}
-
-void plotly::reconnectStream()
-{
-    while(!dry_run && (!socket || !socket->is_connected())) {
-        if(log_level<4) fprintf(stderr,"... Disconnected from streaming servers\n");
-        closeStream();
-        openStream();
+        delete *(sockets+stream);
+        *(sockets+stream)=NULL;
     }
 }
 
-void plotly::jsonStart(int i)
+void plotly::reconnectStream(int number)
 {
-    // Print the length of the message in hex:
-    // 15 char for the json that wraps the data: {"x": , "y": }\n
-    // + 23 char for the token: , "token": "abcdefghij"
-    // = 38
-    printHex_(i+38);
-    print_("\r\n{\"x\": ");
-}
-void plotly::jsonMiddle()
-{
-    print_(", \"y\": ");
-}
-void plotly::jsonEnd(char *token)
-{
-    print_(", \"streamtoken\": \"");
-    print_(token);
-    print_("\"");
-    print_("}\n\r\n");
+    while(!dry_run && (!(*(sockets+number)) || !(*(sockets+number))->is_connected())) {
+        if(log_level<4) fprintf(stderr,"... Disconnected from streaming servers\n");
+        closeStream(number);
+        openStream(number);
+    }
 }
 
-int plotly::len_(int i)
-{
-    // int range: -32,768 to 32,767
-    if(i > 9999) return 5;
-    else if(i > 999) return 4;
-    else if(i > 99) return 3;
-    else if(i > 9) return 2;
-    else if(i > -1) return 1;
-    else if(i > -10) return 2;
-    else if(i > -100) return 3;
-    else if(i > -1000) return 4;
-    else if(i > -10000) return 5;
-    else return 6;
-}
-int plotly::len_(unsigned long i)
+
+void plotly::plot(unsigned long x, int y, int stream)
 {
-    // max length of unsigned long: 4294967295
-    if(i > 999999999) return 10;
-    else if(i > 99999999) return 9;
-    else if(i > 9999999) return 8;
-    else if(i > 999999) return 7;
-    else if(i > 99999) return 6;
-    else if(i > 9999) return 5;
-    else if(i > 999) return 4;
-    else if(i > 99) return 3;
-    else if(i > 9) return 2;
-    else return 1;
+    if (!initalised)
+        return;
+
+    reconnectStream(stream);
+
+    int len = snprintf(buffer,k_bufferSize,"{\"x\": %lu, \"y\": %d}\n", x,y);
+    len = snprintf(buffer,k_bufferSize,"%x\r\n{\"x\": %lu, \"y\": %d}\n\r\n",len, x,y);
+    sendFormatedText(buffer,len,stream);
 }
-int plotly::len_(char *i)
-{
-    return strlen(i);
-}
-void plotly::plot(unsigned long x, int y, char *token)
+
+void plotly::plot(unsigned long x, float y, int stream)
 {
     if (!initalised)
         return;
 
-    reconnectStream();
+    reconnectStream(stream);
 
-//    int len = snprintf(buffer,k_bufferSize,"{\"x\": %lu, \"y\": %d, \"streamtoken\": \"%s\"}\n", x,y,token);
-//    len = snprintf(buffer,k_bufferSize,"%X\r\n{\"x\": %lu, \"y\": %d, \"streamtoken\": \"%s\"}\n\r\n",len, x,y,token);
-    int len = snprintf(buffer,k_bufferSize,"{\"x\": %lu, \"y\": %d}\n", x,y);
-    len = snprintf(buffer,k_bufferSize,"%x\r\n{\"x\": %lu, \"y\": %d}\n\r\n",len, x,y);
-    sendFormatedText(buffer,len);
+    int len = snprintf(buffer,k_bufferSize,"{\"x\": %lu, \"y\": %.3f}\n", x,y);
+    len = snprintf(buffer,k_bufferSize,"%x\r\n{\"x\": %lu, \"y\": %.3f}\n\r\n",len, x,y);
+    sendFormatedText(buffer,len,stream);
 }
 
-void plotly::plot(unsigned long x, float y, char *token)
+void plotly::plot(float x, float y, int stream)
 {
     if (!initalised)
         return;
 
-    reconnectStream();
+    reconnectStream(stream);
 
-    int len = snprintf(buffer,k_bufferSize,"{\"x\": %lu, \"y\": %.3f}\n", x,y);
-    len = snprintf(buffer,k_bufferSize,"%x\r\n{\"x\": %lu, \"y\": %.3f}\n\r\n",len, x,y);
-//    int len = snprintf(buffer,k_bufferSize,"{\"x\": %lu, \"y\": %.3f, \"streamtoken\": \"%s\"}\n", x,y,token);
-//    len = snprintf(buffer,k_bufferSize,"%X\r\n{\"x\": %lu, \"y\": %.3f, \"streamtoken\": \"%s\"}\n\r\n",len, x,y,token);
-    sendFormatedText(buffer,len);
+    int len = snprintf(buffer,k_bufferSize,"{\"x\": %.3f, \"y\": %.3f}\n", x,y);
+    len = snprintf(buffer,k_bufferSize,"%x\r\n{\"x\": %.3f, \"y\": %.3f}\n\r\n",len, x,y);
+    sendFormatedText(buffer,len,stream);
 }
 
-bool plotly::print_(int d)
+
+bool plotly::print_(int d, int stream)
 {
     int32_t len = snprintf(buffer,k_bufferSize,"%d",d);
-    return sendFormatedText(buffer,len);
+    return sendFormatedText(buffer,len,stream);
 }
 
-bool plotly::print_(unsigned long d)
+bool plotly::print_(unsigned long d, int stream)
 {
     int32_t len = snprintf(buffer,k_bufferSize,"%lu",d);
-    return sendFormatedText(buffer,len);
+    return sendFormatedText(buffer,len,stream);
 }
 
-bool plotly::print_(float d)
+bool plotly::print_(float d, int stream)
 {
     int32_t len = snprintf(buffer,k_bufferSize,"%f",d);
-    return sendFormatedText(buffer,len);
+    return sendFormatedText(buffer,len,stream);
 }
 
-bool plotly::print_(char *d)
+bool plotly::print_(const char *d, int stream)
 {
     int32_t len = snprintf(buffer,k_bufferSize,"%s",d);
-    return sendFormatedText(buffer,len);
+    return sendFormatedText(buffer,len,stream);
 }
 
-bool plotly::printHex_(uint16_t d)
+bool plotly::printHex_(uint16_t d, int stream)
 {
     int32_t len = snprintf(buffer,k_bufferSize,"%X",d);
-    return sendFormatedText(buffer,len);
+    return sendFormatedText(buffer,len,stream);
 }
 
-bool plotly::sendFormatedText(char* data, int size)
+bool plotly::sendFormatedText(char* data, int size, int stream)
 {
     if(log_level < 2) {
         fprintf(stderr,"%s",data);
     }
     if(!dry_run) {
-        if (!socket) {
+        if (!*(sockets+stream)) {
             fprintf(stderr,"\nTX failed, No network socket exists\n");
             return false;
-            }
-        if (!(socket->is_connected())) {
+        }
+        if (!((*(sockets+stream))->is_connected())) {
             fprintf(stderr,"\nTX failed, Network socket not connected\n");
-            return false;           
-            }
-        
-        int32_t sent = socket->send_all(data,size);
+            return false;
+        }
+
+        int32_t sent = (*(sockets+stream))->send_all(data,size);
         if (sent == size)
             return true;
         else {
             fprintf(stderr,"\nTX failed to send _%s_ Sent %d of %d bytes\n",data,sent,size);
-            echoRxData();
             return false;
         }
     } else
         return true;
 }
 
-void plotly::echoRxData()
-{
-
-    int32_t dataIn = socket->receive(buffer,k_bufferSize -1);
-    if (dataIn < 0) {
-        if (socket->is_connected()) {
-            fprintf(stderr,"error reading network socket. Closing it\n");
-            socket->close();
-            delete socket;
-            socket = NULL;
-            }
-        else {
-            fprintf(stderr,"error reading network socket, socket isn't connected\n");
-            delete socket;
-            socket = NULL;
-            }
-    }
-    if(dataIn > 0) {
-        buffer[dataIn]=0;
-        fprintf(stderr,"Rx Data __");
-        fprintf(stderr,buffer);
-        fprintf(stderr,"__\n");
-    }
-    
-}
\ No newline at end of file